@designbasekorea/ui-wc 0.9.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE-WORLD-MAP.md +10 -0
  3. package/README.md +60 -0
  4. package/dist/esm/components/db-accordion.d.ts +22 -10
  5. package/dist/esm/components/db-accordion.js +1 -1
  6. package/dist/esm/components/db-achievement-list.d.ts +2 -0
  7. package/dist/esm/components/db-achievement-list.js +1 -1
  8. package/dist/esm/components/db-activity-heatmap.d.ts +1 -0
  9. package/dist/esm/components/db-activity-heatmap.js +1 -1
  10. package/dist/esm/components/db-ad-banner.d.ts +2 -0
  11. package/dist/esm/components/db-ad-banner.js +1 -1
  12. package/dist/esm/components/db-ai-chat-list.d.ts +5 -1
  13. package/dist/esm/components/db-ai-chat-list.js +1 -1
  14. package/dist/esm/components/db-ai-chat.d.ts +2 -0
  15. package/dist/esm/components/db-ai-chat.js +1 -1
  16. package/dist/esm/components/db-ai-file-tree.js +1 -1
  17. package/dist/esm/components/db-alert.d.ts +1 -0
  18. package/dist/esm/components/db-alert.js +1 -1
  19. package/dist/esm/components/db-audio-player.d.ts +2 -0
  20. package/dist/esm/components/db-audio-player.js +1 -1
  21. package/dist/esm/components/db-avatar.d.ts +1 -0
  22. package/dist/esm/components/db-avatar.js +1 -1
  23. package/dist/esm/components/db-banner.d.ts +2 -0
  24. package/dist/esm/components/db-banner.js +1 -1
  25. package/dist/esm/components/db-button.d.ts +40 -2
  26. package/dist/esm/components/db-button.js +1 -1
  27. package/dist/esm/components/db-calendar.d.ts +5 -0
  28. package/dist/esm/components/db-calendar.js +1 -1
  29. package/dist/esm/components/db-card.d.ts +1 -0
  30. package/dist/esm/components/db-card.js +1 -1
  31. package/dist/esm/components/db-chat-composer.d.ts +3 -0
  32. package/dist/esm/components/db-chat-composer.js +1 -1
  33. package/dist/esm/components/db-chat-message.d.ts +2 -0
  34. package/dist/esm/components/db-chat-message.js +1 -1
  35. package/dist/esm/components/db-chat-orb.d.ts +13 -0
  36. package/dist/esm/components/db-chat-orb.js +1 -1
  37. package/dist/esm/components/db-chat-room.d.ts +1 -0
  38. package/dist/esm/components/db-chat-room.js +1 -1
  39. package/dist/esm/components/db-chat-typing.d.ts +2 -0
  40. package/dist/esm/components/db-chat-typing.js +1 -1
  41. package/dist/esm/components/db-chat-widget.d.ts +9 -0
  42. package/dist/esm/components/db-chat-widget.js +1 -1
  43. package/dist/esm/components/db-checkbox.d.ts +3 -0
  44. package/dist/esm/components/db-checkbox.js +1 -1
  45. package/dist/esm/components/db-chip.d.ts +2 -0
  46. package/dist/esm/components/db-chip.js +1 -1
  47. package/dist/esm/components/db-code-block.d.ts +1 -0
  48. package/dist/esm/components/db-code-block.js +1 -1
  49. package/dist/esm/components/db-color-picker.d.ts +4 -0
  50. package/dist/esm/components/db-color-picker.js +1 -1
  51. package/dist/esm/components/db-color-swatch.d.ts +3 -0
  52. package/dist/esm/components/db-color-swatch.js +1 -1
  53. package/dist/esm/components/db-confetti.d.ts +11 -5
  54. package/dist/esm/components/db-confetti.js +1 -1
  55. package/dist/esm/components/db-cookie-banner.d.ts +3 -0
  56. package/dist/esm/components/db-cookie-banner.js +1 -1
  57. package/dist/esm/components/db-countdown.d.ts +2 -1
  58. package/dist/esm/components/db-countdown.js +1 -1
  59. package/dist/esm/components/db-date-picker.d.ts +3 -0
  60. package/dist/esm/components/db-date-picker.js +1 -1
  61. package/dist/esm/components/db-dropdown.d.ts +1 -0
  62. package/dist/esm/components/db-dropdown.js +1 -1
  63. package/dist/esm/components/db-dropzone.d.ts +2 -0
  64. package/dist/esm/components/db-dropzone.js +1 -1
  65. package/dist/esm/components/db-email-verify.d.ts +37 -7
  66. package/dist/esm/components/db-email-verify.js +1 -1
  67. package/dist/esm/components/db-file-uploader.d.ts +2 -0
  68. package/dist/esm/components/db-file-uploader.js +1 -1
  69. package/dist/esm/components/db-floating-action-button.d.ts +1 -1
  70. package/dist/esm/components/db-floating-action-button.js +1 -1
  71. package/dist/esm/components/db-form.d.ts +2 -0
  72. package/dist/esm/components/db-form.js +1 -1
  73. package/dist/esm/components/db-gantt.d.ts +2 -0
  74. package/dist/esm/components/db-gantt.js +1 -1
  75. package/dist/esm/components/db-image-list.d.ts +3 -0
  76. package/dist/esm/components/db-image-list.js +1 -1
  77. package/dist/esm/components/db-image.d.ts +1 -0
  78. package/dist/esm/components/db-image.js +1 -1
  79. package/dist/esm/components/db-input-group.d.ts +30 -0
  80. package/dist/esm/components/db-input-group.js +1 -0
  81. package/dist/esm/components/db-input.d.ts +9 -1
  82. package/dist/esm/components/db-input.js +1 -1
  83. package/dist/esm/components/db-kanban.d.ts +3 -0
  84. package/dist/esm/components/db-kanban.js +1 -1
  85. package/dist/esm/components/db-lightbox.d.ts +2 -0
  86. package/dist/esm/components/db-lightbox.js +1 -1
  87. package/dist/esm/components/db-list.d.ts +2 -0
  88. package/dist/esm/components/db-list.js +1 -1
  89. package/dist/esm/components/db-markdown-editor.d.ts +2 -0
  90. package/dist/esm/components/db-markdown-editor.js +1 -1
  91. package/dist/esm/components/db-menu-item.js +1 -1
  92. package/dist/esm/components/db-navbar.d.ts +6 -0
  93. package/dist/esm/components/db-navbar.js +1 -1
  94. package/dist/esm/components/db-node.d.ts +1 -0
  95. package/dist/esm/components/db-node.js +1 -1
  96. package/dist/esm/components/db-onboarding-modal.d.ts +2 -0
  97. package/dist/esm/components/db-onboarding-modal.js +1 -1
  98. package/dist/esm/components/db-otp-input.d.ts +4 -1
  99. package/dist/esm/components/db-otp-input.js +1 -1
  100. package/dist/esm/components/db-page-header.d.ts +11 -0
  101. package/dist/esm/components/db-page-header.js +1 -1
  102. package/dist/esm/components/db-phone-verify.d.ts +33 -11
  103. package/dist/esm/components/db-phone-verify.js +1 -1
  104. package/dist/esm/components/db-popover.d.ts +1 -0
  105. package/dist/esm/components/db-popover.js +1 -1
  106. package/dist/esm/components/db-progress-step.d.ts +2 -0
  107. package/dist/esm/components/db-progress-step.js +1 -1
  108. package/dist/esm/components/db-radio.d.ts +3 -0
  109. package/dist/esm/components/db-radio.js +1 -1
  110. package/dist/esm/components/db-range-slider.d.ts +36 -7
  111. package/dist/esm/components/db-range-slider.js +1 -1
  112. package/dist/esm/components/db-rating.d.ts +1 -0
  113. package/dist/esm/components/db-rating.js +1 -1
  114. package/dist/esm/components/db-search-bar.d.ts +2 -0
  115. package/dist/esm/components/db-search-bar.js +1 -1
  116. package/dist/esm/components/db-section-logo-cloud.js +1 -1
  117. package/dist/esm/components/db-select.d.ts +35 -6
  118. package/dist/esm/components/db-select.js +1 -1
  119. package/dist/esm/components/db-share.d.ts +5 -0
  120. package/dist/esm/components/db-share.js +1 -1
  121. package/dist/esm/components/db-sidebar.d.ts +3 -0
  122. package/dist/esm/components/db-sidebar.js +1 -1
  123. package/dist/esm/components/db-signature.d.ts +2 -0
  124. package/dist/esm/components/db-signature.js +1 -1
  125. package/dist/esm/components/db-spinner.d.ts +2 -0
  126. package/dist/esm/components/db-spinner.js +1 -1
  127. package/dist/esm/components/db-stepper.d.ts +2 -0
  128. package/dist/esm/components/db-stepper.js +1 -1
  129. package/dist/esm/components/db-table.d.ts +2 -0
  130. package/dist/esm/components/db-table.js +1 -1
  131. package/dist/esm/components/db-tabs.d.ts +37 -13
  132. package/dist/esm/components/db-tabs.js +1 -1
  133. package/dist/esm/components/db-textarea.d.ts +13 -1
  134. package/dist/esm/components/db-textarea.js +1 -1
  135. package/dist/esm/components/db-ticket.js +1 -1
  136. package/dist/esm/components/db-time-picker.d.ts +2 -0
  137. package/dist/esm/components/db-time-picker.js +1 -1
  138. package/dist/esm/components/db-timeline.d.ts +4 -0
  139. package/dist/esm/components/db-timeline.js +1 -1
  140. package/dist/esm/components/db-toast.d.ts +2 -0
  141. package/dist/esm/components/db-toast.js +1 -1
  142. package/dist/esm/components/db-toggle.d.ts +33 -4
  143. package/dist/esm/components/db-toggle.js +1 -1
  144. package/dist/esm/components/db-tool-layout.d.ts +92 -0
  145. package/dist/esm/components/db-tool-layout.js +1 -0
  146. package/dist/esm/components/db-tool-panel.d.ts +50 -0
  147. package/dist/esm/components/db-tool-panel.js +1 -0
  148. package/dist/esm/components/db-top-banner.d.ts +2 -0
  149. package/dist/esm/components/db-top-banner.js +1 -1
  150. package/dist/esm/components/db-two-factor.d.ts +35 -12
  151. package/dist/esm/components/db-two-factor.js +1 -1
  152. package/dist/esm/components/db-video-player.d.ts +3 -0
  153. package/dist/esm/components/db-video-player.js +1 -1
  154. package/dist/esm/components/db-world-map.d.ts +60 -0
  155. package/dist/esm/components/db-world-map.js +1 -0
  156. package/dist/esm/components/db-youtube-player.d.ts +2 -0
  157. package/dist/esm/components/db-youtube-player.js +1 -1
  158. package/dist/esm/components/db-zoom-control.d.ts +52 -0
  159. package/dist/esm/components/db-zoom-control.js +1 -0
  160. package/dist/esm/components/input-validation.js +1 -1
  161. package/dist/esm/components/paper-card.d.ts +2 -0
  162. package/dist/esm/components/paper-card.js +1 -1
  163. package/dist/esm/core/control-label.js +1 -0
  164. package/dist/esm/core/i18n-copy.js +1 -1
  165. package/dist/esm/core/i18n-en.js +1 -1
  166. package/dist/esm/core/i18n.d.ts +31 -1
  167. package/dist/esm/core/i18n.js +1 -1
  168. package/dist/esm/core/range-values.js +1 -0
  169. package/dist/esm/core/text-field-state.js +1 -0
  170. package/dist/esm/helpers/world-countries.js +1 -0
  171. package/dist/esm/helpers/world-land.js +1 -0
  172. package/dist/esm/index.d.ts +6 -1
  173. package/dist/esm/index.js +1 -1
  174. package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -1
  175. package/dist/index.cjs +1 -1
  176. package/dist/index.css +1 -1
  177. package/dist/index.d.ts +804 -83
  178. package/dist/index.esm.js +1 -1
  179. package/dist/react/index.cjs +1 -1
  180. package/dist/react/index.d.ts +6211 -648
  181. package/dist/react/index.esm.js +1 -1
  182. package/package.json +9 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # 변경 이력
2
2
 
3
+ ## 0.11.0 - 2026-11-03
4
+
5
+ - 다크모드 중립 Tooltip의 글자·배경 대비와 화살표 표면 연결 수정.
6
+ - InputGroup 아이콘 버튼 예제: 좌측·우측·양쪽 액션, Tooltip 조합, s/m/l 정렬 및 실제 버튼 disabled 연결.
7
+
8
+ - InputGroup 추가: 좌우 Button·Select 결합, 공유 테두리/높이/포커스, 그룹 라벨·도움말·오류·disabled 및 네이티브 폼 동작 유지.
9
+ - ToolPanel 제목 우측 actions 슬롯 예제·스타일·가이드 추가. 폼 예제의 중복 DemoSection을 공통 헤더·본문 구조로 통일.
10
+
11
+ - Tools: `db-zoom-control` / `ZoomControl` 추가. 비율 메뉴·확대/축소·화면 맞춤·전체화면, 범위·단계 설정 및 캔버스에 한정된 키보드 단축키 제공. `xs` 24px, 기능 선택 `controls`, 단순 +/− `zoom-style="plain"` 지원.
12
+ - ToolButton 툴팁 trigger의 기준선 차이로 아이콘·텍스트 버튼의 수직 중심이 달라지던 문제 수정.
13
+ - 다크모드에서 `tone="dark"` DesignToolbar의 배경과 아이콘 대비 수정.
14
+
15
+ ## 0.10.0 - 2026-10-29
16
+
17
+ - Navbar·PageHeader의 반응형 배치, sticky 레이어 및 메뉴 표시 개선.
18
+ - 폼 컨트롤과 Button·Accordion·Tabs의 포커스, 키보드, 접근성·상태 처리 보강.
19
+ - React Accordion·Tabs의 패널 콘텐츠와 내부 상태 보존, 이벤트 연결 개선. React wrapper 사용 시 React와 React DOM을 함께 설치하세요.
20
+ - 최신 컴포넌트·스타일·타입 및 공통 계약 ui-contracts 0.2.0을 함께 제공.
21
+
22
+
3
23
  ## 0.9.4 - 2026-10-27
4
24
 
5
25
  Workspace 템플릿 인박스·프로젝트 탭에서 발견한 레이아웃 버그 수정 릴리스입니다.
@@ -0,0 +1,10 @@
1
+ # World map geographic data
2
+
3
+ The geographic outlines in `world-land.ts` derive from Natural Earth `ne_110m_land` (1:110m land polygons). Coordinates are rounded to three decimal places; the solid globe also includes simplified country boundaries from `ne_110m_admin_0_countries`. Boundaries are for visual context and do not express a political position. Maps are decorative geographic overviews, not navigation tools.
4
+
5
+ Source: https://github.com/nvkelso/natural-earth-vector/blob/master/geojson/ne_110m_land.geojson
6
+ Country data: https://github.com/nvkelso/natural-earth-vector/blob/master/geojson/ne_110m_admin_0_countries.geojson
7
+ Terms: https://www.naturalearthdata.com/about/terms-of-use/
8
+ Natural Earth geographic data is public domain. Made with Natural Earth.
9
+
10
+ The UI component and its projection/interaction code follow the ui-wc package license.
package/README.md CHANGED
@@ -34,6 +34,8 @@ npm run verify:docs # 문서 ↔ 구현 정합성
34
34
 
35
35
  ```bash
36
36
  npm install @designbasekorea/ui-wc @designbasekorea/tokens @designbasekorea/theme
37
+ # React apps also need matching react and react-dom (18.x or 19.x).
38
+ npm install react react-dom
37
39
  ```
38
40
 
39
41
  ## 기본 사용법
@@ -546,3 +548,61 @@ import { PageHeader, Select, SegmentControl, Sidebar } from '@designbasekorea/ui
546
548
  ```
547
549
 
548
550
  `sticky`는 가장 가까운 스크롤 영역의 상단에 고정합니다. `compactOnScroll`은 원래 위치를 지나면 설명과 격자를 숨기고 제목·개수·컨트롤을 유지합니다. 상단 앱 바가 있으면 헤더의 `--db-page-header-sticky-offset`에 해당 높이(예: `64px`)를 지정하세요. 별도 자바스크립트 스크롤 핸들러는 필요하지 않습니다. `Sidebar`의 `navigation` 타입은 문서·페이지 탐색에 사용합니다.
551
+
552
+ ### Zoom Control · Tools
553
+
554
+ `db-zoom-control` / React `ZoomControl` combines a percentage menu, zoom in/out, fit and fullscreen. It composes the official DesignToolbar and ToolButton. Set `target-id`, `content-width` and `content-height` to calculate fit. The consumer applies `db-change.detail.value / 100` to its content transform; content dimensions are the **untransformed** dimensions. `value` is a percentage (default 100), bounded by `min=10` and `max=400`; `step=25` controls button increments. `fit-padding=24` reserves space at each edge. In fit mode a ResizeObserver keeps the scale in sync with the target size. An external value change exits fit mode. Without content dimensions, handle `db-fit` to compute your own fit.
555
+
556
+ The target (parent by default) needs `tabindex="0"` to receive shortcuts: `+`/`−` zoom, `Shift+0` fit, `Shift+1` 100%, `Shift+F` fullscreen. Shortcuts only run while focus is in that target or the control. Text fields, editable content, composing input, menus and Ctrl/Cmd/Alt combinations are excluded. When controls share a target, the first enabled control containing the action owns that shortcut. `shortcuts="false"` (React `shortcuts={false}`) opts out. Arrow/Home/End navigation and menu focus are inherited from DesignToolbar/ToolButton.
557
+
558
+ `disabled`, `hide-fullscreen`, `size="xs|s|m"`, `tone="default|dark"` are available. Fullscreen uses the standard browser API, follows Escape exits, disables when unavailable and emits `db-error` on rejection. `db-fullscreen-change` reports target state. React maps these to `onChange`, `onFit`, `onFullscreenChange`, `onError`. Programmatic value changes do not emit `db-change`. Korean/English labels use the existing `dbUseLocale` / `dbSetLocale` API; consumers do not need to copy translation tables.
559
+
560
+ Choose a smaller `size="xs"` (24px buttons), `s` (28px), or `m` (32px). `controls` chooses actions in canonical order: `['out','value','in','fit','fullscreen']`. Examples: `controls={['out','value','in']}` excludes Fit/fullscreen; `zoomStyle="plain"` switches the combined toolbar to simple minus/plus icons while retaining percentage, Fit and fullscreen; `controls={['fit','fullscreen']}` omits zoom completely. For HTML, serialize `controls='["out","value","in","fit","fullscreen"]'` and `zoom-style="plain"`. Hidden actions do not respond to shortcuts; Fit is also removed from the percentage menu when omitted. Split controls sharing one target delegate each shortcut to the first enabled instance containing that action.
561
+
562
+ ### Input Group · joined fields
563
+
564
+ Use `InputGroup` / `db-input-group` for a field with leading/trailing Button or Select. Keep one consumer-owned `<div slot="control">` containing one unlabelled Input and its companion controls. This wrapper stays in place across renders; native values, selection, input events and form participation remain with the child components.
565
+
566
+ ```tsx
567
+ <InputGroup label="Website" helperText={protocol + domain} size="m">
568
+ <div slot="control">
569
+ <Select aria-label="Protocol" options={protocols} value={protocol} onChange={e => setProtocol(String(e.detail.value))} />
570
+ <Input name="domain" value={domain} onInput={e => setDomain(e.detail.value)} />
571
+ <Button variant="tertiary" onPress={save}>Apply</Button>
572
+ </div>
573
+ </InputGroup>
574
+ ```
575
+
576
+ `size="s|m|l"` sets a consistent field height. `disabled` disables all three control kinds and preserves individually disabled children when re-enabled. `label` focuses/names the main Input. `required`/`error` apply to that Input; `helper-text`/`error-message` render below the combined field and are linked by `aria-describedby`. Give Selects and icon-only buttons their own accessible labels. Child labels/helper text belong outside the shared control row; use group-level labels/messages. Buttons keep independent focus, Select keeps its listbox keyboard behavior. Event handlers are attached to the individual child; the group does not synthesize a combined value.
577
+
578
+ Icon actions use the official `Button iconOnly` inside `Tooltip focus-target="child"` on either side of the Input. The group sizes tooltip-wrapped and direct icon buttons consistently at s/m/l, preserves the outer corners and propagates disabled to the actual button. Give each button an `aria-label` and localize its tooltip.
579
+
580
+ ```tsx
581
+ <InputGroup label="Search">
582
+ <div slot="control">
583
+ <Input value={query} onInput={e => setQuery(e.detail.value)} />
584
+ <Tooltip content="Search projects" focus-target="child">
585
+ <Button iconOnly variant="primary" aria-label="Search projects" onPress={search}>
586
+ <SearchIcon size={16} aria-hidden />
587
+ </Button>
588
+ </Tooltip>
589
+ </div>
590
+ </InputGroup>
591
+ ```
592
+
593
+ The Input Group example includes a trailing search action, a leading reference-ID action and paired reset/copy actions. Copy handles clipboard rejection with a visible message.
594
+
595
+ ### Tool Panel header actions
596
+
597
+ Place one container in `slot="actions"` to add toggles, buttons or menus to the right of `heading`. It can coexist with `closable`. Actions remain available when a collapsible panel is collapsed, and clicking an action does not toggle the panel. Connect child handlers/state yourself; the slot does not own business state.
598
+
599
+ ```tsx
600
+ <ToolPanel heading="Fill" collapsible appearance="card" closable>
601
+ <div slot="actions">
602
+ <Toggle size="s" aria-label="Enable fill" checked={enabled} onChange={e => setEnabled(e.detail.checked)} />
603
+ <Button size="s" variant="ghost" onPress={reset}>Reset</Button>
604
+ </div>
605
+ <div slot="body"><Input label="Color" disabled={!enabled} value={color} onInput={e => setColor(e.detail.value)} /></div>
606
+ </ToolPanel>
607
+ ```
608
+
@@ -1,7 +1,7 @@
1
+ type ExpandMode = 'multiple' | 'single' | 'all';
1
2
  interface AccordionItemConfig {
2
3
  id?: string;
3
4
  title: string;
4
- /** 슬롯(content-N)이 없을 때 본문으로 렌더되는 텍스트 */
5
5
  content?: string;
6
6
  disabled?: boolean;
7
7
  itemType?: 'icon' | 'number' | 'question' | 'none';
@@ -9,28 +9,39 @@ interface AccordionItemConfig {
9
9
  }
10
10
  declare class DbAccordion extends HTMLElement {
11
11
  static get observedAttributes(): string[];
12
+ private readonly uid;
13
+ private readonly panels;
14
+ private readonly slotNodes;
12
15
  private expanded;
13
- private isRendering;
14
- private suppressExpandedAttrCallback;
16
+ private rendering;
17
+ private reflecting;
15
18
  private childObserver?;
19
+ private stash?;
16
20
  get items(): AccordionItemConfig[];
17
21
  set items(value: AccordionItemConfig[] | string);
22
+ get size(): string;
23
+ set size(value: string);
24
+ get variant(): string;
25
+ set variant(value: string);
18
26
  get allowMultiple(): boolean;
19
27
  set allowMultiple(value: boolean);
28
+ get expandMode(): ExpandMode;
29
+ set expandMode(value: ExpandMode);
20
30
  get expandedItems(): string[];
21
31
  set expandedItems(value: string[] | string);
22
32
  connectedCallback(): void;
23
33
  disconnectedCallback(): void;
24
- attributeChangedCallback(name: string): void;
25
- /** id와 index 문자열 충돌 방지 — 항목당 단일 키만 사용 */
26
- private resolveItemKey;
27
- private getExpandMode;
34
+ attributeChangedCallback(name: string, oldValue: string | null, value: string | null): void;
35
+ /** Refresh externally supplied slot structure without changing expansion or emitting events. */
36
+ refresh(): void;
37
+ focus(options?: FocusOptions): void;
38
+ private entries;
28
39
  private syncExpandedFromAttributes;
29
- private iconSize;
30
- private collectPanelContent;
31
- private isItemExpanded;
40
+ private collectSlots;
41
+ private createPanel;
32
42
  private syncExpandedState;
33
43
  private render;
44
+ private navigate;
34
45
  private toggle;
35
46
  }
36
47
  declare global {
@@ -40,3 +51,4 @@ declare global {
40
51
  }
41
52
 
42
53
  export { DbAccordion };
54
+ export type { AccordionItemConfig };
@@ -1 +1 @@
1
- import{dbText as e}from"../core/i18n.js";import{createDbIcon as t}from"./db-icons.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{ACCORDION_VARIANTS as n,ACCORDION_SIZES as i}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbAccordion extends HTMLElement{constructor(){super(...arguments),this.expanded=new Set,this.isRendering=!1,this.suppressExpandedAttrCallback=!1}static get observedAttributes(){return["items","variant","size","allow-multiple","expand-mode","expanded-items","default-item-type"]}get items(){try{const e=JSON.parse(this.getAttribute("items")||"[]");return Array.isArray(e)?e:[]}catch{return[]}}set items(e){this.setAttribute("items","string"==typeof e?e:JSON.stringify(e))}get allowMultiple(){return this.hasAttribute("allow-multiple")}set allowMultiple(e){this.toggleAttribute("allow-multiple",e)}get expandedItems(){const e=(this.getAttribute("expanded-items")||"").trim();if(!e)return[];if("all"===e)return this.items.map((e,t)=>this.resolveItemKey(e,t));try{const t=JSON.parse(e);if(Array.isArray(t))return t.map(e=>String(e).trim()).filter(Boolean)}catch{}return e.split(",").map(e=>e.trim()).filter(Boolean)}set expandedItems(e){Array.isArray(e)?this.setAttribute("expanded-items",e.join(",")):this.setAttribute("expanded-items",e)}connectedCallback(){this.syncExpandedFromAttributes(),this.render(),this.childObserver=new MutationObserver(e=>{if(this.isRendering)return;e.some(e=>Array.from(e.addedNodes).some(e=>e instanceof HTMLElement&&e.hasAttribute("slot")))&&this.render()}),this.childObserver.observe(this,{childList:!0})}disconnectedCallback(){this.childObserver?.disconnect()}attributeChangedCallback(e){if(this.isConnected){if("expanded-items"===e){if(this.suppressExpandedAttrCallback)return;return this.syncExpandedFromAttributes(),void this.syncExpandedState()}return"expand-mode"===e||"allow-multiple"===e?(this.syncExpandedFromAttributes(),void this.syncExpandedState()):"items"===e?(this.syncExpandedFromAttributes(),void this.render()):void this.render()}}resolveItemKey(e,t){return e.id??String(t)}getExpandMode(){const e=this.getAttribute("expand-mode");return"single"===e||"multiple"===e||"all"===e?e:(this.hasAttribute("allow-multiple"),"multiple")}syncExpandedFromAttributes(){const e=this.items.map((e,t)=>this.resolveItemKey(e,t)),t=this.expandedItems,s=this.getExpandMode();t.length>0?this.expanded=new Set(t.filter(t=>e.includes(t))):this.expanded="all"!==s?new Set:new Set(e)}iconSize(e){return"s"===e?16:"l"===e?24:20}collectPanelContent(){const e=new Map,register=(t,s)=>{if(!(t instanceof HTMLElement)||"SLOT"===t.tagName)return;e.has(s)||e.set(s,[]);const n=e.get(s);n.includes(t)||n.push(t)};return Array.from(this.children).forEach(e=>{const t=e.getAttribute("slot")?.match(/^content-(\d+)$/);t&&register(e,Number(t[1]))}),this.querySelectorAll(":scope > .designbase-wc-accordion__item").forEach((e,t)=>{const s=e.querySelector(":scope > .designbase-wc-accordion__content > .designbase-wc-accordion__content-inner");s&&Array.from(s.children).forEach(e=>register(e,t))}),e}isItemExpanded(e,t){return this.expanded.has(this.resolveItemKey(e,t))}syncExpandedState(){const e=this.items;this.querySelectorAll(":scope > .designbase-wc-accordion__item").forEach((t,s)=>{const n=e[s];if(!n)return;const i=this.isItemExpanded(n,s);t.classList.toggle("designbase-wc-accordion__item--expanded",i),t.classList.toggle("designbase-wc-accordion__item--disabled",Boolean(n.disabled));const a=t.querySelector(".designbase-wc-accordion__trigger"),d=t.querySelector(".designbase-wc-accordion__content");a?.setAttribute("aria-expanded",String(i)),d?.setAttribute("aria-hidden",String(!i))})}render(){this.isRendering=!0;const a=this.collectPanelContent(),d=s(this.getAttribute("variant"),n,"default"),r=s(this.getAttribute("size"),i,"m"),o=this.getExpandMode(),c=this.getAttribute("default-item-type")||"none",l=this.items,m=this.iconSize(r);this.className=["designbase-wc-accordion",`designbase-wc-accordion--${d}`,`designbase-wc-accordion--${r}`,"multiple"===o&&"designbase-wc-accordion--multiple","single"===o&&"designbase-wc-accordion--single","all"===o&&"designbase-wc-accordion--all"].filter(Boolean).join(" "),this.setAttribute("role","region"),this.setAttribute("aria-label",e("아코디언")),this.querySelectorAll(":scope > .designbase-wc-accordion__item").forEach(e=>e.remove()),l.forEach((e,s)=>{const n=this.resolveItemKey(e,s),i=this.isItemExpanded(e,s),r=document.createElement("div");r.className=["designbase-wc-accordion__item",`designbase-wc-accordion__item--${d}`,i&&"designbase-wc-accordion__item--expanded",e.disabled&&"designbase-wc-accordion__item--disabled"].filter(Boolean).join(" "),r.dataset.itemKey=n;const o=document.createElement("button");o.type="button",o.className="designbase-wc-accordion__trigger",o.disabled=Boolean(e.disabled),o.setAttribute("aria-expanded",String(i)),o.setAttribute("aria-controls",`accordion-content-${n}`),o.addEventListener("click",()=>{e.disabled||this.toggle(n)});const l=e.itemType||c;if("number"===l){const e=document.createElement("div");e.className="designbase-wc-accordion__number",e.textContent=`${s+1}.`,o.append(e)}else if("question"===l){const e=document.createElement("div");e.className="designbase-wc-accordion__question",e.textContent="Q.",o.append(e)}else if("icon"===l&&e.icon){const s=document.createElement("div");s.className="designbase-wc-accordion__icon",s.append(t(e.icon,m)),o.append(s)}const p=document.createElement("span");p.className="designbase-wc-accordion__title",p.textContent=e.title;const h=document.createElement("div");h.className="designbase-wc-accordion__chevron",h.append(t("chevron-down",m)),o.append(p,h);const u=document.createElement("div");u.id=`accordion-content-${n}`,u.className="designbase-wc-accordion__content",u.setAttribute("aria-hidden",String(!i));const b=document.createElement("div");b.className="designbase-wc-accordion__content-inner";const g=a.get(s)||[];if(g.length>0)g.forEach(e=>{e.removeAttribute("slot"),b.append(e)});else if(e.content){const t=document.createElement("p");t.className="designbase-wc-accordion__content-text",t.textContent=e.content,b.append(t)}u.append(b),r.append(o,u),this.append(r)}),this.isRendering=!1}toggle(e){const t=this.getExpandMode();this.expanded.has(e)?this.expanded.delete(e):"single"===t?this.expanded=new Set([e]):this.expanded.add(e);const s=Array.from(this.expanded);this.syncExpandedState(),this.suppressExpandedAttrCallback=!0,this.expandedItems=s,this.suppressExpandedAttrCallback=!1,this.dispatchEvent(new CustomEvent("db-change",{detail:{expandedItems:s},bubbles:!0,composed:!0}))}}customElements.get("db-accordion")||customElements.define("db-accordion",DbAccordion);export{DbAccordion};
1
+ import{dbText as e}from"../core/i18n.js";import{dbUid as t}from"../core/uid.js";import{setHostClasses as s}from"../core/host-class.js";import{createDbIcon as n,isDbIconName as i}from"./db-icons.js";import{coerceToContract as r}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{ACCORDION_SIZES as a,ACCORDION_VARIANTS as o}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbAccordion extends HTMLElement{constructor(){super(...arguments),this.uid=t("db-accordion"),this.panels=new Map,this.slotNodes=new Set,this.expanded=new Set,this.rendering=!1,this.reflecting=!1}static get observedAttributes(){return["items","variant","size","allow-multiple","expand-mode","expanded-items","default-item-type"]}get items(){try{const e=JSON.parse(this.getAttribute("items")||"[]");return Array.isArray(e)?e.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.title):[]}catch{return[]}}set items(e){this.setAttribute("items","string"==typeof e?e:JSON.stringify(e))}get size(){return r(this.getAttribute("size"),a,"m")}set size(e){this.setAttribute("size",e)}get variant(){return r(this.getAttribute("variant"),o,"default")}set variant(e){this.setAttribute("variant",e)}get allowMultiple(){return this.hasAttribute("allow-multiple")}set allowMultiple(e){this.toggleAttribute("allow-multiple",e)}get expandMode(){return r(this.getAttribute("expand-mode"),["multiple","single","all"],"multiple")}set expandMode(e){this.setAttribute("expand-mode",e)}get expandedItems(){return this.isConnected||this.syncExpandedFromAttributes(),[...this.expanded]}set expandedItems(e){this.setAttribute("expanded-items",Array.isArray(e)?JSON.stringify(e):e)}connectedCallback(){for(const e of["items","size","variant","allowMultiple","expandMode","expandedItems"])if(Object.prototype.hasOwnProperty.call(this,e)){const t=this[e];delete this[e],this[e]=t}this.syncExpandedFromAttributes(),this.render(),this.childObserver??(this.childObserver=new MutationObserver(e=>{if(this.rendering)return;e.some(e=>"attributes"===e.type||Array.from(e.addedNodes).some(e=>e instanceof HTMLElement&&e.parentElement===this&&/^content-\d+$/.test(e.getAttribute("slot")||""))||Array.from(e.removedNodes).some(e=>e instanceof HTMLElement&&(this.slotNodes.has(e)&&!this.contains(e)||e.parentElement!==this&&[...this.panels.values()].some(t=>t.wrapper===e))))&&this.render()})),this.childObserver.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["slot"]})}disconnectedCallback(){this.childObserver?.disconnect()}attributeChangedCallback(e,t,s){this.isConnected&&!this.reflecting&&t!==s&&(["items","expand-mode","allow-multiple","expanded-items"].includes(e)&&this.syncExpandedFromAttributes(),"expanded-items"===e?this.syncExpandedState():this.render())}refresh(){this.isConnected&&this.render()}focus(e){[...this.panels.values()].find(e=>!e.trigger.disabled)?.trigger.focus(e)}entries(){const e=new Set;return this.items.map((t,s)=>{const n="string"==typeof t.id&&t.id.length?t.id:String(s);let i=n,r=1;for(;e.has(i);)i=`${n}#${++r}`;return e.add(i),{item:t,index:s,key:i}})}syncExpandedFromAttributes(){const e=this.entries().map(e=>e.key),t=this.getAttribute("expanded-items");let s=[];if(null===t)s="all"===this.expandMode?e:[];else if("all"===t.trim())s=e;else try{const e=JSON.parse(t);s=Array.isArray(e)?e.filter(e=>"string"==typeof e||"number"==typeof e).map(String):t.split(",").map(e=>e.trim()).filter(Boolean)}catch{s=t.split(",").map(e=>e.trim()).filter(Boolean)}const n=[...new Set(s)].filter(t=>e.includes(t));this.expanded=new Set("single"===this.expandMode?n.slice(0,1):n)}collectSlots(){for(const e of Array.from(this.children))e instanceof HTMLElement&&/^content-\d+$/.test(e.getAttribute("slot")||"")&&this.slotNodes.add(e);const e=new Map;for(const t of this.slotNodes){if(!this.contains(t)){this.slotNodes.delete(t);continue}const s=t.getAttribute("slot")?.match(/^content-(\d+)$/);if(!s)continue;const n=Number(s[1]),i=e.get(n)||[];i.push(t),e.set(n,i)}return e}createPanel(e){const s=document.createElement("div");s.className="designbase-wc-accordion__item",s.dataset.itemKey=e;const i=document.createElement("button");i.type="button",i.className="designbase-wc-accordion__trigger";const r=document.createElement("div");r.className="designbase-wc-accordion__content";const a=t(this.uid);i.id=`${a}-trigger`,r.id=`${a}-content`,i.setAttribute("aria-controls",r.id),r.setAttribute("role","region"),r.setAttribute("aria-labelledby",i.id);const o=document.createElement("span");o.setAttribute("aria-hidden","true");const d=document.createElement("span");d.className="designbase-wc-accordion__title";const c=document.createElement("span");c.className="designbase-wc-accordion__chevron",c.setAttribute("aria-hidden","true"),c.append(n("chevron-down",20)),i.append(o,d,c),i.addEventListener("click",()=>this.toggle(e)),i.addEventListener("keydown",t=>this.navigate(t,e));const l=document.createElement("div");return l.className="designbase-wc-accordion__content-inner",l.id=`${a}-inner`,r.append(l),s.append(i,r),{wrapper:s,trigger:i,title:d,prefix:o,content:r,inner:l}}syncExpandedState(){for(const[e,t]of this.panels){const s=this.expanded.has(e);!s&&t.content.contains(document.activeElement)&&t.trigger.focus(),t.wrapper.classList.toggle("designbase-wc-accordion__item--expanded",s),t.trigger.setAttribute("aria-expanded",String(s)),t.content.setAttribute("aria-hidden",String(!s)),t.content.inert=!s}}render(){if(!this.rendering){this.rendering=!0;try{const t=this.collectSlots(),a=this.entries(),o=document.activeElement,d=[...this.panels.values()].findIndex(e=>e.wrapper.contains(o)),c=new Set(a.map(e=>e.key));s(this,["designbase-wc-accordion",`designbase-wc-accordion--${this.variant}`,`designbase-wc-accordion--${this.size}`,`designbase-wc-accordion--${this.expandMode}`]),this.setAttribute("role","region"),this.hasAttribute("aria-label")||this.hasAttribute("aria-labelledby")||this.setAttribute("aria-label",e("아코디언"));const l=new Set;let h=null;for(const{item:e,index:s,key:o}of a){const a=this.panels.get(o)||this.createPanel(o);this.panels.set(o,a),a.wrapper.className=`designbase-wc-accordion__item designbase-wc-accordion__item--${this.variant}`,a.wrapper.classList.toggle("designbase-wc-accordion__item--disabled",Boolean(e.disabled)),a.trigger.disabled=Boolean(e.disabled),a.title.textContent=e.title,a.inner.dataset.dbAccordionContentIndex=String(s);const d=r(e.itemType||this.getAttribute("default-item-type"),["icon","number","question","none"],"none"),c="s"===this.size?16:"l"===this.size?24:20,p=`${d}:${e.icon}:${s}:${c}`;if(a.iconSignature!==p){if(a.prefix.replaceChildren(),a.prefix.hidden="none"===d||!["icon","number","question"].includes(d),a.prefix.className=`designbase-wc-accordion__${d}`,"number"===d)a.prefix.textContent=`${s+1}.`;else if("question"===d)a.prefix.textContent="Q.";else if("icon"===d){const t=e.icon;"string"==typeof t&&i(t)&&a.prefix.append(n(t,c))}a.iconSignature=p}if(!a.inner.hasAttribute("data-db-react-content")){const n=t.get(s)||[];for(const e of Array.from(a.inner.children))e===a.fallback||n.includes(e)||(this.stash??(this.stash=Object.assign(document.createElement("div"),{hidden:!0,inert:!0})),this.stash.parentElement||this.append(this.stash),this.stash.append(e));if(n.length){a.fallback?.remove();for(const e of n)l.add(e),e.parentElement!==a.inner&&a.inner.append(e)}else void 0!==e.content&&""!==e.content?(a.fallback??(a.fallback=Object.assign(document.createElement("p"),{className:"designbase-wc-accordion__content-text"})),a.fallback.dataset.dbGenerated="",a.fallback.textContent="string"==typeof e.content?e.content:String(e.content),a.fallback.parentElement!==a.inner&&a.inner.append(a.fallback)):a.fallback?.remove()}const m=h?h.nextElementSibling:this.firstElementChild;m!==a.wrapper&&this.insertBefore(a.wrapper,m),h=a.wrapper}for(const[e,t]of this.panels)if(!c.has(e)){for(const e of this.slotNodes)t.wrapper.contains(e)&&!l.has(e)&&(this.stash??(this.stash=Object.assign(document.createElement("div"),{hidden:!0,inert:!0})),this.stash.parentElement||this.append(this.stash),this.stash.append(e));t.wrapper.remove(),this.panels.delete(e)}for(const e of this.slotNodes)l.has(e)||(this.stash??(this.stash=Object.assign(document.createElement("div"),{hidden:!0,inert:!0})),this.stash.parentElement||this.append(this.stash),e.parentElement!==this.stash&&this.stash.append(e));const p=a.map(({key:e})=>[e,this.panels.get(e)]);this.panels.clear();for(const[e,t]of p)this.panels.set(e,t);if(this.syncExpandedState(),o instanceof HTMLElement&&o!==document.body&&document.activeElement!==o&&d>=0){const e=[...this.panels.values()].filter(e=>!e.trigger.disabled);!o.isConnected||o.closest("[inert]")||o instanceof HTMLButtonElement&&o.disabled?e[Math.min(d,e.length-1)]?.trigger.focus():o.focus()}}finally{this.rendering=!1}this.dispatchEvent(new Event("db-content-targets-change"))}}navigate(e,t){if(!["ArrowDown","ArrowUp","Home","End"].includes(e.key)||e.altKey||e.ctrlKey||e.metaKey)return;const s=[...this.panels.entries()].filter(([,e])=>!e.trigger.disabled),n=s.findIndex(([e])=>e===t);if(n<0)return;e.preventDefault();const i="Home"===e.key?0:"End"===e.key?s.length-1:(n+("ArrowDown"===e.key?1:-1)+s.length)%s.length;s[i]?.[1].trigger.focus()}toggle(e){if(!this.panels.get(e)?.trigger.disabled){this.expanded.has(e)?this.expanded.delete(e):"single"===this.expandMode?this.expanded=new Set([e]):this.expanded.add(e),this.syncExpandedState(),this.reflecting=!0;try{this.expandedItems=[...this.expanded]}finally{this.reflecting=!1}this.dispatchEvent(new CustomEvent("db-change",{detail:{expandedItems:[...this.expanded]},bubbles:!0,composed:!0}))}}}customElements.get("db-accordion")||customElements.define("db-accordion",DbAccordion);export{DbAccordion};
@@ -14,10 +14,12 @@ interface DbAchievementItem {
14
14
  /** Presentation only. The owner supplies server-validated awards/progress; selection grants nothing. */
15
15
  declare class DbAchievementList extends DbElement {
16
16
  private data;
17
+ private unsubscribeLocale?;
17
18
  static get observedAttributes(): string[];
18
19
  get items(): DbAchievementItem[];
19
20
  set items(value: DbAchievementItem[]);
20
21
  connectedCallback(): void;
22
+ disconnectedCallback(): void;
21
23
  attributeChangedCallback(): void;
22
24
  private render;
23
25
  }
@@ -1 +1 @@
1
- import{dbText as t}from"../core/i18n.js";import{DbElement as e}from"../core/base-element.js";import{textNode as i,replaceView as s}from"../core/credit-ui.js";import{createDbIcon as a}from"./db-icons.js";import"./db-button.js";function achievementItems(t){const e=new Set;return Array.isArray(t)?t.filter(t=>!(!t||"string"!=typeof t.id||!t.id||e.has(t.id)||"string"!=typeof t.title||"string"!=typeof t.description||!["locked","earned","needs_review"].includes(t.state))&&(e.add(t.id),!0)).map(t=>({id:t.id,title:t.title,description:t.description,state:t.state,current:Number.isSafeInteger(t.current)&&t.current>=0?t.current:void 0,target:Number.isSafeInteger(t.target)&&t.target>0?t.target:void 0,earnedLabel:"string"==typeof t.earnedLabel?t.earnedLabel:void 0,motif:["star","award","bulb","heart"].includes(t.motif||"")?t.motif:"award"})):[]}function achievementMedallion(t){const e=i("div","designbase-wc-achievement-art","");return e.dataset.state=t.state,e.setAttribute("aria-hidden","true"),e.append(a(t.motif||"award",40)),e}class DbAchievementList extends e{constructor(){super(...arguments),this.data=[]}static get observedAttributes(){return["label","empty-label","loading","error"]}get items(){return this.data.map(t=>({...t}))}set items(t){this.data=achievementItems(t),this.isConnected&&this.render()}connectedCallback(){this.upgradeProperties(["items"]),this.cleanups.listen(this,"db-click",t=>{const e=t.target instanceof HTMLElement?t.target.closest("[data-badge-id]"):null;e&&this.contains(e)&&!this.hasAttribute("loading")&&!this.hasAttribute("error")&&this.dbEmit("select",{id:e.dataset.badgeId})}),this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){this.classList.add("designbase-wc-achievement-list"),this.setAttribute("aria-busy",String(this.hasAttribute("loading")));const e=i("div","designbase-wc-achievement-list__grid","");e.setAttribute("role","list"),e.setAttribute("aria-label",this.getAttribute("label")||"뱃지 목록");const a=this.getAttribute("error")||(this.hasAttribute("loading")?"뱃지를 불러오고 있어요":this.data.length?"":this.getAttribute("empty-label")||"아직 등록된 뱃지가 없어요");if(a){const t=i("p","designbase-wc-achievement-list__message",a);return t.setAttribute("role",this.hasAttribute("error")?"alert":"status"),void s(this,t)}for(const s of this.data){const a=i("article","designbase-wc-achievement-list__card","");if(a.dataset.state=s.state,a.setAttribute("role","listitem"),a.append(achievementMedallion(s),i("span","designbase-wc-achievement-list__state","earned"===s.state?"획득 완료":"needs_review"===s.state?"기록 확인 필요":"도전 중"),i("h3","",s.title),i("p","designbase-wc-achievement-list__description",s.description)),"locked"===s.state&&s.target){const t=document.createElement("progress");t.max=s.target,t.value=Math.min(s.current||0,s.target),t.setAttribute("aria-label",s.title+" 진행률"),a.append(t,i("span","designbase-wc-achievement-list__date",`${Math.min(s.current||0,s.target)} / ${s.target}`))}else a.append(i("span","designbase-wc-achievement-list__date","earned"===s.state?s.earnedLabel||"나의 컬렉션에 담았어요":"needs_review"===s.state?"연결된 기록을 다시 확인해 주세요":"다음 순간을 기다려요"));const r=document.createElement("db-button");r.setAttribute("variant","secondary"),r.setAttribute("size","s"),r.dataset.badgeId=s.id,r.dataset.action=s.id,r.textContent=t("자세히 보기"),r.setAttribute("aria-label",s.title+" 자세히 보기"),a.append(r),e.append(a)}s(this,e)}}customElements.get("db-achievement-list")||customElements.define("db-achievement-list",DbAchievementList);export{DbAchievementList,achievementItems,achievementMedallion};
1
+ import{subscribeDbLocale as e,dbText as t}from"../core/i18n.js";import{DbElement as i}from"../core/base-element.js";import{textNode as s,replaceView as a}from"../core/credit-ui.js";import{createDbIcon as r}from"./db-icons.js";import"./db-button.js";function achievementItems(e){const t=new Set;return Array.isArray(e)?e.filter(e=>!(!e||"string"!=typeof e.id||!e.id||t.has(e.id)||"string"!=typeof e.title||"string"!=typeof e.description||!["locked","earned","needs_review"].includes(e.state))&&(t.add(e.id),!0)).map(e=>({id:e.id,title:e.title,description:e.description,state:e.state,current:Number.isSafeInteger(e.current)&&e.current>=0?e.current:void 0,target:Number.isSafeInteger(e.target)&&e.target>0?e.target:void 0,earnedLabel:"string"==typeof e.earnedLabel?e.earnedLabel:void 0,motif:["star","award","bulb","heart"].includes(e.motif||"")?e.motif:"award"})):[]}function achievementMedallion(e){const t=s("div","designbase-wc-achievement-art","");return t.dataset.state=e.state,t.setAttribute("aria-hidden","true"),t.append(r(e.motif||"award",40)),t}class DbAchievementList extends i{constructor(){super(...arguments),this.data=[]}static get observedAttributes(){return["label","empty-label","loading","error"]}get items(){return this.data.map(e=>({...e}))}set items(e){this.data=achievementItems(e),this.isConnected&&this.render()}connectedCallback(){this.upgradeProperties(["items"]),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.render()),this.cleanups.listen(this,"db-click",e=>{const t=e.target instanceof HTMLElement?e.target.closest("[data-badge-id]"):null;t&&this.contains(t)&&!this.hasAttribute("loading")&&!this.hasAttribute("error")&&this.dbEmit("select",{id:t.dataset.badgeId})}),this.render()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,super.disconnectedCallback()}attributeChangedCallback(){this.isConnected&&this.render()}render(){this.classList.add("designbase-wc-achievement-list"),this.setAttribute("aria-busy",String(this.hasAttribute("loading")));const e=s("div","designbase-wc-achievement-list__grid","");e.setAttribute("role","list"),e.setAttribute("aria-label",this.getAttribute("label")||t("뱃지 목록"));const i=this.getAttribute("error")||(this.hasAttribute("loading")?t("뱃지를 불러오고 있어요"):this.data.length?"":this.getAttribute("empty-label")||t("아직 등록된 뱃지가 없어요"));if(i){const e=s("p","designbase-wc-achievement-list__message",i);return e.setAttribute("role",this.hasAttribute("error")?"alert":"status"),void a(this,e)}for(const i of this.data){const a=s("article","designbase-wc-achievement-list__card","");if(a.dataset.state=i.state,a.setAttribute("role","listitem"),a.append(achievementMedallion(i),s("span","designbase-wc-achievement-list__state",t("earned"===i.state?"획득 완료":"needs_review"===i.state?"기록 확인 필요":"도전 중")),s("h3","",i.title),s("p","designbase-wc-achievement-list__description",i.description)),"locked"===i.state&&i.target){const e=document.createElement("progress");e.max=i.target,e.value=Math.min(i.current||0,i.target),e.setAttribute("aria-label",i.title+" "+t("진행률")),a.append(e,s("span","designbase-wc-achievement-list__date",`${Math.min(i.current||0,i.target)} / ${i.target}`))}else a.append(s("span","designbase-wc-achievement-list__date","earned"===i.state?i.earnedLabel||t("나의 컬렉션에 담았어요"):t("needs_review"===i.state?"연결된 기록을 다시 확인해 주세요":"다음 순간을 기다려요")));const r=document.createElement("db-button");r.setAttribute("variant","secondary"),r.setAttribute("size","s"),r.dataset.badgeId=i.id,r.dataset.action=i.id,r.textContent=t("자세히 보기"),r.setAttribute("aria-label",i.title+" "+t("자세히 보기")),a.append(r),e.append(a)}a(this,e)}}customElements.get("db-achievement-list")||customElements.define("db-achievement-list",DbAchievementList);export{DbAchievementList,achievementItems,achievementMedallion};
@@ -8,6 +8,7 @@ type DbActivityHeatmapCellDetail = {
8
8
  level: number;
9
9
  };
10
10
  declare class DbActivityHeatmap extends HTMLElement {
11
+ private unsubscribeLocale?;
11
12
  static get observedAttributes(): string[];
12
13
  connectedCallback(): void;
13
14
  disconnectedCallback(): void;
@@ -1 +1 @@
1
- import{getDbLocale as e,dbText as t}from"../core/i18n.js";import{sanitizeHref as a}from"../core/safe-url.js";import{ACTIVITY_HEATMAP_SIZES as s,ACTIVITY_HEATMAP_COLOR_SCHEMES as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r="designbase-wc-activity-heatmap";class DbActivityHeatmap extends HTMLElement{constructor(){super(...arguments),this.handleClick=e=>{const t=e.target,a=t?.closest(`.${r}__cell`);if(!a||a.disabled)return;const s=a.dataset.date;if(!s)return;const n=Number(a.dataset.value??"0"),l=Number(a.dataset.level??"0");this.dispatchEvent(new CustomEvent("db-cell-click",{detail:{date:s,value:n,level:l},bubbles:!0,composed:!0}))}}static get observedAttributes(){return["data","end-date","weeks","week-starts-on","size","locale","color-scheme","show-legend","show-month-labels","show-day-labels","legend-less-label","legend-more-label","value-label","max-value","footer-text","footer-href"]}connectedCallback(){this.addEventListener("click",this.handleClick),this.render()}disconnectedCallback(){this.removeEventListener("click",this.handleClick)}attributeChangedCallback(){this.render()}parseData(){const e=this.getAttribute("data");if(!e)return[];try{const t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>e&&"string"==typeof e.date&&"number"==typeof e.value):[]}catch{return[]}}parseEndDate(){const e=this.getAttribute("end-date");if(e){const t=this.parseISODate(e);if(t)return t}const t=new Date;return new Date(t.getFullYear(),t.getMonth(),t.getDate())}parseISODate(e){const t=/^(\d{4})-(\d{2})-(\d{2})/.exec(e.trim());if(!t)return null;const a=Number(t[1]),s=Number(t[2]),n=Number(t[3]);return a&&s&&n?new Date(a,s-1,n):null}formatISODate(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}addDays(e,t){const a=new Date(e);return a.setDate(a.getDate()+t),a}startOfWeek(e,t){const a=(e.getDay()-t+7)%7;return this.addDays(e,-a)}isAfter(e,t){return e.getTime()>t.getTime()}buildWeeks(e,t,a){const s=this.startOfWeek(e,a),n=this.addDays(s,7*-(t-1)),r=[];for(let e=0;e<t;e+=1){const t=[];for(let a=0;a<7;a+=1)t.push(this.addDays(n,7*e+a));r.push(t)}return r}resolveLevel(e,t,a){if(e<=0||t<=0)return 0;const s=e/t;return Math.min(a,Math.max(1,Math.ceil(s*a)))}formatMonthLabel(e,t){return new Intl.DateTimeFormat(t,{month:"short"}).format(e)}formatTooltip(e,t,a,s){const n=new Intl.DateTimeFormat(a,{year:"numeric",month:"long",day:"numeric"}).format(e);return t<=0?`${n}: 활동 없음`:`${n}: ${t.toLocaleString(a)}${s}`}getDayLabels(e,t){const a=Array.from({length:7},(a,s)=>{const n=new Date(2024,0,7+(e+s)%7);return new Intl.DateTimeFormat(t,{weekday:"short"}).format(n)}),s=1===e?[0,2,4,6]:[1,3,5,0];return a.map((e,t)=>s.includes(t)?e:"")}getMonthLabels(e,t){const a=e.map(e=>{const t=e.find(e=>e.getDate()<=7)??e[0];return t.getMonth()+12*t.getFullYear()}),s=e.map(()=>"");let n=0;for(;n<e.length;){const r=a[n];let l=n+1;for(;l<e.length&&a[l]===r;)l+=1;if(l-n>=2){const a=e[n],r=a.find(e=>e.getDate()<=7)??a[0];s[n]=this.formatMonthLabel(r,t)}n=l}return s}render(){const l=this.parseData(),o=new Map(l.map(e=>[e.date,e.value])),i=this.parseEndDate(),c=Math.max(1,Number(this.getAttribute("weeks")??"53")||53),d="0"===this.getAttribute("week-starts-on")?0:1,m=this.getAttribute("size")||"m",u=s.includes(m)?m:"m",h=this.getAttribute("locale")||("ko"===e()?"ko-KR":"en-US"),p=this.getAttribute("color-scheme")||"green",b=n.includes(p)?p:"green",g="false"!==this.getAttribute("show-legend"),f="false"!==this.getAttribute("show-month-labels"),_="false"!==this.getAttribute("show-day-labels"),v=this.getAttribute("legend-less-label")||"적음",$=this.getAttribute("legend-more-label")||"많음",E=this.getAttribute("value-label")||"회",A=this.getAttribute("footer-text")||"",D=this.getAttribute("footer-href")||"",y=this.getAttribute("max-value"),N=l.map(e=>e.value).filter(e=>e>0),k=N.length?Math.max(...N):0,w=y?Math.max(Number(y)||0,k):k,x=this.buildWeeks(i,c,d),C=this.getMonthLabels(x,h),M=this.getDayLabels(d,h);this.className=[r,`${r}--size-${u}`,`${r}--color-${b}`,g&&`${r}--with-legend`].filter(Boolean).join(" "),this.replaceChildren();const L=document.createElement("div");L.className=`${r}__viewport`;const S=document.createElement("div");if(S.className=`${r}__scroll-inner`,_){const e=document.createElement("div");e.className=`${r}__sticky-rail`;const t=document.createElement("div");t.className=`${r}__sticky-spacer`,f||t.classList.add(`${r}__sticky-spacer--hidden`);const a=document.createElement("div");a.className=`${r}__days`,a.setAttribute("aria-hidden","true"),M.forEach(e=>{const t=document.createElement("span");t.className=`${r}__day`,t.textContent=e,a.append(t)}),e.append(t,a),S.append(e)}const T=document.createElement("div");if(T.className=`${r}__grid-area`,f){const e=document.createElement("div");e.className=`${r}__months`,e.setAttribute("aria-hidden","true"),C.forEach(t=>{const a=document.createElement("span");a.className=`${r}__month`,a.textContent=t,e.append(a)}),T.append(e)}const I=document.createElement("div");if(I.className=`${r}__weeks`,I.setAttribute("role","grid"),I.setAttribute("aria-label",t("활동 히트맵")),x.forEach(e=>{const t=document.createElement("div");t.className=`${r}__week`,t.setAttribute("role","row"),e.forEach(e=>{const a=this.formatISODate(e),s=this.isAfter(e,i),n=s?0:o.get(a)??0,l=s?0:this.resolveLevel(n,w,4),c=document.createElement("button");c.type="button",c.className=[`${r}__cell`,`${r}__cell--level-${l}`,s&&`${r}__cell--future`].filter(Boolean).join(" "),c.dataset.date=a,c.dataset.value=String(n),c.dataset.level=String(l),c.title=this.formatTooltip(e,n,h,E),c.setAttribute("aria-label",this.formatTooltip(e,n,h,E)),c.disabled=s,t.append(c)}),I.append(t)}),T.append(I),S.append(T),L.append(S),this.append(L),g||A){const e=document.createElement("div");if(e.className=`${r}__footer`,A)if(D){const t=document.createElement("a");t.className=`${r}__footer-link`,t.href=a(D,"#"),t.textContent=A,e.append(t)}else{const t=document.createElement("span");t.className=`${r}__footer-text`,t.textContent=A,e.append(t)}else e.append(document.createElement("span"));if(g){const t=document.createElement("div");t.className=`${r}__legend`,t.setAttribute("aria-hidden","true");const a=document.createElement("span");a.className=`${r}__legend-label`,a.textContent=v;const s=document.createElement("div");s.className=`${r}__legend-scale`;for(let e=0;e<=4;e+=1){const t=document.createElement("span");t.className=`${r}__legend-cell ${r}__legend-cell--level-${e}`,s.append(t)}const n=document.createElement("span");n.className=`${r}__legend-label`,n.textContent=$,t.append(a,s,n),e.append(t)}this.append(e)}}}customElements.get("db-activity-heatmap")||customElements.define("db-activity-heatmap",DbActivityHeatmap);export{DbActivityHeatmap};
1
+ import{subscribeDbLocale as e,dbText as t,getDbLocale as a}from"../core/i18n.js";import{sanitizeHref as s}from"../core/safe-url.js";import{ACTIVITY_HEATMAP_SIZES as n,ACTIVITY_HEATMAP_COLOR_SCHEMES as r}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const l="designbase-wc-activity-heatmap";class DbActivityHeatmap extends HTMLElement{constructor(){super(...arguments),this.handleClick=e=>{const t=e.target,a=t?.closest(`.${l}__cell`);if(!a||a.disabled)return;const s=a.dataset.date;if(!s)return;const n=Number(a.dataset.value??"0"),r=Number(a.dataset.level??"0");this.dispatchEvent(new CustomEvent("db-cell-click",{detail:{date:s,value:n,level:r},bubbles:!0,composed:!0}))}}static get observedAttributes(){return["data","end-date","weeks","week-starts-on","size","locale","color-scheme","show-legend","show-month-labels","show-day-labels","legend-less-label","legend-more-label","value-label","max-value","footer-text","footer-href"]}connectedCallback(){this.addEventListener("click",this.handleClick),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>{const e=this.contains(document.activeElement)?document.activeElement.dataset.date:void 0;this.render(),e&&Array.from(this.querySelectorAll(`.${l}__cell`)).find(t=>t.dataset.date===e)?.focus()}),this.render()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,this.removeEventListener("click",this.handleClick)}attributeChangedCallback(){this.render()}parseData(){const e=this.getAttribute("data");if(!e)return[];try{const t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>e&&"string"==typeof e.date&&"number"==typeof e.value):[]}catch{return[]}}parseEndDate(){const e=this.getAttribute("end-date");if(e){const t=this.parseISODate(e);if(t)return t}const t=new Date;return new Date(t.getFullYear(),t.getMonth(),t.getDate())}parseISODate(e){const t=/^(\d{4})-(\d{2})-(\d{2})/.exec(e.trim());if(!t)return null;const a=Number(t[1]),s=Number(t[2]),n=Number(t[3]);return a&&s&&n?new Date(a,s-1,n):null}formatISODate(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}addDays(e,t){const a=new Date(e);return a.setDate(a.getDate()+t),a}startOfWeek(e,t){const a=(e.getDay()-t+7)%7;return this.addDays(e,-a)}isAfter(e,t){return e.getTime()>t.getTime()}buildWeeks(e,t,a){const s=this.startOfWeek(e,a),n=this.addDays(s,7*-(t-1)),r=[];for(let e=0;e<t;e+=1){const t=[];for(let a=0;a<7;a+=1)t.push(this.addDays(n,7*e+a));r.push(t)}return r}resolveLevel(e,t,a){if(e<=0||t<=0)return 0;const s=e/t;return Math.min(a,Math.max(1,Math.ceil(s*a)))}formatMonthLabel(e,t){return new Intl.DateTimeFormat(t,{month:"short"}).format(e)}formatTooltip(e,s,n,r){const l=new Intl.DateTimeFormat(n,{year:"numeric",month:"long",day:"numeric"}).format(e);if(s<=0)return`${l}: ${t("활동 없음")}`;const i=this.hasAttribute("value-label")||"en"!==a()||1!==s?r:" time";return`${l}: ${s.toLocaleString(n)}${i}`}getDayLabels(e,t){const a=Array.from({length:7},(a,s)=>{const n=new Date(2024,0,7+(e+s)%7);return new Intl.DateTimeFormat(t,{weekday:"short"}).format(n)}),s=1===e?[0,2,4,6]:[1,3,5,0];return a.map((e,t)=>s.includes(t)?e:"")}getMonthLabels(e,t){const a=e.map(e=>{const t=e.find(e=>e.getDate()<=7)??e[0];return t.getMonth()+12*t.getFullYear()}),s=e.map(()=>"");let n=0;for(;n<e.length;){const r=a[n];let l=n+1;for(;l<e.length&&a[l]===r;)l+=1;if(l-n>=2){const a=e[n],r=a.find(e=>e.getDate()<=7)??a[0];s[n]=this.formatMonthLabel(r,t)}n=l}return s}render(){const e=this.parseData(),i=new Map(e.map(e=>[e.date,e.value])),o=this.parseEndDate(),c=Math.max(1,Number(this.getAttribute("weeks")??"53")||53),d="0"===this.getAttribute("week-starts-on")?0:1,m=this.getAttribute("size")||"m",u=n.includes(m)?m:"m",h=this.getAttribute("locale")||("ko"===a()?"ko-KR":"en-US"),b=this.getAttribute("color-scheme")||"green",p=r.includes(b)?b:"green",f="false"!==this.getAttribute("show-legend"),g="false"!==this.getAttribute("show-month-labels"),_="false"!==this.getAttribute("show-day-labels"),v=this.getAttribute("legend-less-label")||t("적음"),$=this.getAttribute("legend-more-label")||t("많음"),E=this.getAttribute("value-label")||`${"en"===a()?" ":""}${t("회")}`,A=this.getAttribute("footer-text")||"",y=this.getAttribute("footer-href")||"",D=this.getAttribute("max-value"),N=e.map(e=>e.value).filter(e=>e>0),k=N.length?Math.max(...N):0,w=D?Math.max(Number(D)||0,k):k,x=this.buildWeeks(o,c,d),L=this.getMonthLabels(x,h),C=this.getDayLabels(d,h);this.className=[l,`${l}--size-${u}`,`${l}--color-${p}`,f&&`${l}--with-legend`].filter(Boolean).join(" "),this.replaceChildren();const M=document.createElement("div");M.className=`${l}__viewport`;const S=document.createElement("div");if(S.className=`${l}__scroll-inner`,_){const e=document.createElement("div");e.className=`${l}__sticky-rail`;const t=document.createElement("div");t.className=`${l}__sticky-spacer`,g||t.classList.add(`${l}__sticky-spacer--hidden`);const a=document.createElement("div");a.className=`${l}__days`,a.setAttribute("aria-hidden","true"),C.forEach(e=>{const t=document.createElement("span");t.className=`${l}__day`,t.textContent=e,a.append(t)}),e.append(t,a),S.append(e)}const T=document.createElement("div");if(T.className=`${l}__grid-area`,g){const e=document.createElement("div");e.className=`${l}__months`,e.setAttribute("aria-hidden","true"),L.forEach(t=>{const a=document.createElement("span");a.className=`${l}__month`,a.textContent=t,e.append(a)}),T.append(e)}const I=document.createElement("div");if(I.className=`${l}__weeks`,I.setAttribute("role","grid"),I.setAttribute("aria-label",t("활동 히트맵")),x.forEach(e=>{const t=document.createElement("div");t.className=`${l}__week`,t.setAttribute("role","row"),e.forEach(e=>{const a=this.formatISODate(e),s=this.isAfter(e,o),n=s?0:i.get(a)??0,r=s?0:this.resolveLevel(n,w,4),c=document.createElement("button");c.type="button",c.className=[`${l}__cell`,`${l}__cell--level-${r}`,s&&`${l}__cell--future`].filter(Boolean).join(" "),c.dataset.date=a,c.dataset.value=String(n),c.dataset.level=String(r),c.title=this.formatTooltip(e,n,h,E),c.setAttribute("aria-label",this.formatTooltip(e,n,h,E)),c.disabled=s,t.append(c)}),I.append(t)}),T.append(I),S.append(T),M.append(S),this.append(M),f||A){const e=document.createElement("div");if(e.className=`${l}__footer`,A)if(y){const t=document.createElement("a");t.className=`${l}__footer-link`,t.href=s(y,"#"),t.textContent=A,e.append(t)}else{const t=document.createElement("span");t.className=`${l}__footer-text`,t.textContent=A,e.append(t)}else e.append(document.createElement("span"));if(f){const t=document.createElement("div");t.className=`${l}__legend`,t.setAttribute("aria-hidden","true");const a=document.createElement("span");a.className=`${l}__legend-label`,a.textContent=v;const s=document.createElement("div");s.className=`${l}__legend-scale`;for(let e=0;e<=4;e+=1){const t=document.createElement("span");t.className=`${l}__legend-cell ${l}__legend-cell--level-${e}`,s.append(t)}const n=document.createElement("span");n.className=`${l}__legend-label`,n.textContent=$,t.append(a,s,n),e.append(t)}this.append(e)}}}customElements.get("db-activity-heatmap")||customElements.define("db-activity-heatmap",DbActivityHeatmap);export{DbActivityHeatmap};
@@ -1,6 +1,7 @@
1
1
  declare class DbAdBanner extends HTMLElement {
2
2
  static get observedAttributes(): string[];
3
3
  private built;
4
+ private unsubscribeLocale?;
4
5
  private visible;
5
6
  private progress;
6
7
  private autoCloseTimer?;
@@ -55,6 +56,7 @@ declare class DbAdBanner extends HTMLElement {
55
56
  private syncLink;
56
57
  /** show-countdown + end-date 가 있을 때만 카운트다운을 마운트한다. */
57
58
  private syncCountdown;
59
+ private syncLocale;
58
60
  private updateUI;
59
61
  }
60
62
  declare global {
@@ -1 +1 @@
1
- import{dbText as t}from"../core/i18n.js";import{createDbIcon as e}from"./db-icons.js";import{sanitizeHref as i}from"../core/safe-url.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{AD_BANNER_VARIANTS as n,AD_BANNER_SIZES as r}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const a="designbase-wc-ad-banner",l={hero:"지금 바로 시작하세요",topbar:"🎉 신규 가입 시 10,000원 적립금 증정!",card:"프리미엄 멤버십",floating:"오늘만 특가!"},o={hero:"최대 50% 할인된 가격으로 프리미엄 서비스를 경험해보세요. 기간 한정 특별 혜택!",topbar:"오늘 하루만 특별 혜택",card:"무제한 서비스 이용권을 특별가에 만나보세요",floating:"장바구니에 담긴 상품 추가 20% 할인"},h={hero:"자세히 보기",topbar:"지금 가입하기",card:"알아보기",floating:"할인받기"},d="star-filled",c={primary:"primary",secondary:"secondary",success:"success",warning:"warning",error:"error"};class DbAdBanner extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.visible=!0,this.progress=100}static get observedAttributes(){return["type","variant","size","title","subtitle","cta-text","image","image-alt","href","target","auto-close","close-delay","background","animation-type","grid-type","use-random-gradient","gradient-scheme","gradient-tone","gradient-animated","countdown-end-date","show-countdown","open"]}connectedCallback(){this.style.display="block",this.built||(this.built=!0,this.build()),this.updateUI()}disconnectedCallback(){this.stopAutoClose()}attributeChangedCallback(t){this.built&&("open"===t&&(this.visible="false"!==this.getAttribute("open")),this.updateUI())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return""===i||"true"===i}getType(){const t=this.getAttribute("type")||"hero";return["hero","topbar","card","floating"].includes(t)?t:"hero"}getVariant(){return s(this.getAttribute("variant"),n,"primary")}getSize(){return s(this.getAttribute("size"),r,"m")}ctaButtonSize(t,e){return"card"===t?"s":"floating"===t?"s"===e?"s":"m":e}build(){this.rootEl=document.createElement("div"),this.contentEl=document.createElement("div"),this.contentEl.className=`${a}__content`,this.closeBtn=document.createElement("button"),this.closeBtn.type="button",this.closeBtn.className=`${a}__close`,this.closeBtn.setAttribute("aria-label",t("닫기")),this.closeBtn.append(e("close",16)),this.closeBtn.addEventListener("click",t=>{t.stopPropagation(),this.handleClose()}),this.imageEl=document.createElement("img"),this.imageEl.className=`${a}__image`,this.imageEl.loading="lazy";const i=document.createElement("div");i.className=`${a}__main`,this.mainEl=i,this.iconEl=document.createElement("div"),this.iconEl.className=`${a}__icon`,this.labelEl=document.createElement("span"),this.labelEl.className=`${a}__label`,this.labelEl.textContent=t("특별 이벤트"),this.badgeEl=document.createElement("span"),this.badgeEl.className=`${a}__badge`,this.badgeEl.textContent="HOT DEAL",this.titleEl=document.createElement("h2"),this.titleEl.className=`${a}__title`,this.subtitleEl=document.createElement("p"),this.subtitleEl.className=`${a}__subtitle`;const s=document.createElement("div");s.className=`${a}__text`,this.textEl=s,this.countdownWrapEl=document.createElement("div"),this.countdownWrapEl.className=`${a}__countdown`,s.append(this.titleEl,this.subtitleEl),this.actionsEl=document.createElement("div"),this.actionsEl.className=`${a}__actions`,this.ctaEl=document.createElement("db-button"),this.ctaEl.className=`${a}__cta`,this.ctaEl.setAttribute("variant","primary"),this.ctaEl.setAttribute("end-icon","arrow-right"),this.ctaEl.addEventListener("click",t=>{t.stopPropagation(),this.dispatchEvent(new CustomEvent("db-cta-click",{bubbles:!0,composed:!0}))}),this.actionsEl.append(this.ctaEl),i.append(s,this.actionsEl),this.progressEl=document.createElement("div"),this.progressEl.className=`${a}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${a}__progress-bar`,this.progressEl.append(this.progressBarEl),this.contentEl.append(this.closeBtn,i,this.progressEl),this.rootEl.append(this.contentEl),this.append(this.rootEl),this.contentEl.addEventListener("click",t=>{"topbar"===this.getType()&&(t.target?.closest(`.${a}__close`)||this.dispatchEvent(new CustomEvent("db-cta-click",{bubbles:!0,composed:!0})))})}handleClose(){this.visible=!1,this.style.display="none",this.setAttribute("open","false"),this.stopAutoClose(),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))}stopAutoClose(){this.autoCloseTimer&&(window.clearInterval(this.autoCloseTimer),this.autoCloseTimer=void 0)}startAutoClose(t){this.stopAutoClose(),this.progress=100,this.autoCloseTimer=window.setInterval(()=>{this.progress-=100/(t/100),this.progressBarEl&&(this.progressBarEl.style.width=`${Math.max(this.progress,0)}%`),this.progress<=0&&this.handleClose()},100)}getBackground(){const t=this.getAttribute("background");return t&&["solid","gradient","animation","grid"].includes(t)?t:this.getBool("use-random-gradient")?"gradient":"solid"}ensureAnimBg(){if(!this.animBgEl){const t=document.createElement("div");t.className=`${a}__backdrop`;const e=document.createElement("db-animation-background");e.className=`${a}__anim-bg`,e.setAttribute("width","100%"),e.setAttribute("height","100%"),t.append(e),this.animBgEl=t,this.animBgInner=e}return this.animBgEl}ensureGridBg(){if(!this.gridBgEl){const t=document.createElement("div");t.className=`${a}__backdrop`;const e=document.createElement("db-grid-background");e.className=`${a}__grid-bg`,e.setAttribute("width","100%"),e.setAttribute("height","100%"),t.append(e),this.gridBgEl=t,this.gridBgInner=e}return this.gridBgEl}clearBackdrop(t){"gradient"!==t&&this.gradientEl&&(this.gradientEl.remove(),this.gradientEl=void 0),"animation"!==t&&this.animBgEl&&(this.animBgEl.remove(),this.animBgEl=void 0,this.animBgInner=void 0),"grid"!==t&&this.gridBgEl&&(this.gridBgEl.remove(),this.gridBgEl=void 0,this.gridBgInner=void 0)}renderBackground(t,e){if(!this.rootEl||!this.contentEl)return;if(this.clearBackdrop(t),"gradient"===t){this.gradientEl||(this.gradientEl=document.createElement("db-random-gradient"),this.gradientEl.className=`${a}__gradient-bg`,this.gradientEl.setAttribute("height","100%"),this.gradientEl.setAttribute("width","100%"));const t=this.getAttribute("gradient-tone")||"vivid",i=!this.hasAttribute("gradient-animated")||"false"!==this.getAttribute("gradient-animated");return this.gradientEl.setAttribute("scheme",this.getAttribute("gradient-scheme")||c[e]),this.gradientEl.setAttribute("tone",t),this.gradientEl.setAttribute("animated",i?"true":"false"),this.gradientEl.parentElement!==this.rootEl&&this.rootEl.replaceChildren(this.gradientEl),void(this.contentEl.parentElement!==this.gradientEl&&this.gradientEl.append(this.contentEl))}const i=[];if("animation"===t){const t=this.ensureAnimBg();this.animBgInner?.setAttribute("type",this.getAttribute("animation-type")||"graphite"),this.animBgInner?.setAttribute("theme","dark"),i.push(t)}else if("grid"===t){const t=this.ensureGridBg();this.gridBgInner?.setAttribute("type",this.getAttribute("grid-type")||"lines"),this.gridBgInner?.setAttribute("color","var(--db-text-inverse-primary)"),this.gridBgInner?.setAttribute("opacity","0.18"),this.gridBgInner?.setAttribute("fade","vignette"),this.gridBgInner?.setAttribute("animated",""),i.push(t)}const s=[...i,this.contentEl],n=Array.from(this.rootEl.children);n.length===s.length&&s.every((t,e)=>n[e]===t)||this.rootEl.replaceChildren(...s)}syncIcon(t){this.mainEl&&this.textEl&&this.iconEl&&("card"===t?(this.iconEl.replaceChildren(e(d,32)),this.iconEl.parentElement!==this.mainEl&&this.mainEl.insertBefore(this.iconEl,this.textEl)):this.iconEl.remove())}syncBadge(t){this.textEl&&this.badgeEl&&("floating"===t?this.badgeEl.parentElement!==this.textEl&&this.textEl.insertBefore(this.badgeEl,this.textEl.firstChild):this.badgeEl.remove())}syncImage(t,e){this.contentEl&&this.imageEl&&this.mainEl&&("floating"===t&&e?(this.imageEl.src=e,this.imageEl.alt=this.getAttribute("image-alt")||"",this.imageEl.parentElement!==this.contentEl&&this.contentEl.insertBefore(this.imageEl,this.mainEl)):this.imageEl.remove())}syncLink(t,e){if(!this.contentEl)return;const s=i(e,"");if("topbar"!==t||!s)return void this.linkEl?.remove();this.linkEl||(this.linkEl=document.createElement("a"),this.linkEl.className=`${a}__link`),this.linkEl.setAttribute("href",s);const n=this.getAttribute("target");n?this.linkEl.setAttribute("target",n):this.linkEl.removeAttribute("target"),this.linkEl.setAttribute("aria-label",this.titleEl?.textContent||"배너 링크"),this.linkEl.parentElement!==this.contentEl&&this.contentEl.append(this.linkEl)}syncCountdown(t,e){this.textEl&&this.countdownWrapEl&&(t&&e?(this.countdownEl||(this.countdownEl=document.createElement("db-countdown"),this.countdownEl.setAttribute("size","s"),this.countdownEl.setAttribute("variant","compact"),this.countdownEl.setAttribute("show-labels","false"),this.countdownWrapEl.replaceChildren(this.countdownEl)),this.countdownEl.setAttribute("end-date",e),this.countdownWrapEl.parentElement!==this.textEl&&this.textEl.append(this.countdownWrapEl)):this.countdownWrapEl.remove())}updateUI(){if(!this.visible||"false"===this.getAttribute("open"))return void(this.style.display="none");this.style.display="block";const t=this.getType(),e=this.getVariant(),i=this.getSize(),s=this.getAttribute("title")||l[t],n=this.getAttribute("subtitle")||o[t],r=this.getAttribute("cta-text")||h[t],d=this.getBool("auto-close"),c=Math.max(Number(this.getAttribute("close-delay")||5e3),1e3),g=this.getBackground(),E=this.getAttribute("gradient-tone")||"vivid",u=this.getBool("show-countdown"),m=this.getAttribute("countdown-end-date");this.rootEl&&(this.rootEl.className=[a,`${a}--type-${t}`,`${a}--variant-${e}`,`${a}--size-${i}`,`${a}--bg-${g}`,d&&`${a}--auto-close`,"gradient"===g&&`${a}--with-gradient`,"gradient"===g&&"light"===E&&`${a}--gradient-light`].filter(Boolean).join(" ")),this.renderBackground(g,e),this.syncIcon(t),this.syncBadge(t),this.syncImage(t,this.getAttribute("image")),this.syncLink(t,this.getAttribute("href")),this.titleEl&&(this.titleEl.textContent=s),this.subtitleEl&&(this.subtitleEl.textContent=n),this.syncCountdown(u,m),this.actionsEl&&(this.actionsEl.hidden="topbar"===t),this.ctaEl&&(this.ctaEl.textContent=r,this.ctaEl.setAttribute("size",this.ctaButtonSize(t,i)),"floating"===t?this.ctaEl.setAttribute("full-width",""):this.ctaEl.removeAttribute("full-width")),this.progressEl&&(this.progressEl.hidden=!d),this.progressBarEl&&d&&(this.progressBarEl.style.width=`${this.progress}%`),this.stopAutoClose(),d&&this.startAutoClose(c)}}customElements.get("db-ad-banner")||customElements.define("db-ad-banner",DbAdBanner);export{DbAdBanner};
1
+ import{subscribeDbLocale as t,dbText as e}from"../core/i18n.js";import{createDbIcon as i}from"./db-icons.js";import{sanitizeHref as s}from"../core/safe-url.js";import{coerceToContract as n}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{AD_BANNER_VARIANTS as r,AD_BANNER_SIZES as l}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const a="designbase-wc-ad-banner",o={hero:"지금 바로 시작하세요",topbar:"🎉 신규 가입 시 10,000원 적립금 증정!",card:"프리미엄 멤버십",floating:"오늘만 특가!"},h={hero:"최대 50% 할인된 가격으로 프리미엄 서비스를 경험해보세요. 기간 한정 특별 혜택!",topbar:"오늘 하루만 특별 혜택",card:"무제한 서비스 이용권을 특별가에 만나보세요",floating:"장바구니에 담긴 상품 추가 20% 할인"},c={hero:"자세히 보기",topbar:"지금 가입하기",card:"알아보기",floating:"할인받기"},d="star-filled",g={primary:"primary",secondary:"secondary",success:"success",warning:"warning",error:"error"};class DbAdBanner extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.visible=!0,this.progress=100}static get observedAttributes(){return["type","variant","size","title","subtitle","cta-text","image","image-alt","href","target","auto-close","close-delay","background","animation-type","grid-type","use-random-gradient","gradient-scheme","gradient-tone","gradient-animated","countdown-end-date","show-countdown","open"]}connectedCallback(){this.style.display="block",this.built||(this.built=!0,this.build()),this.updateUI(),this.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>this.syncLocale())}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,this.stopAutoClose()}attributeChangedCallback(t){this.built&&("open"===t&&(this.visible="false"!==this.getAttribute("open")),this.updateUI())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return""===i||"true"===i}getType(){const t=this.getAttribute("type")||"hero";return["hero","topbar","card","floating"].includes(t)?t:"hero"}getVariant(){return n(this.getAttribute("variant"),r,"primary")}getSize(){return n(this.getAttribute("size"),l,"m")}ctaButtonSize(t,e){return"card"===t?"s":"floating"===t?"s"===e?"s":"m":e}build(){this.rootEl=document.createElement("div"),this.contentEl=document.createElement("div"),this.contentEl.className=`${a}__content`,this.closeBtn=document.createElement("button"),this.closeBtn.type="button",this.closeBtn.className=`${a}__close`,this.closeBtn.setAttribute("aria-label",e("닫기")),this.closeBtn.append(i("close",16)),this.closeBtn.addEventListener("click",t=>{t.stopPropagation(),this.handleClose()}),this.imageEl=document.createElement("img"),this.imageEl.className=`${a}__image`,this.imageEl.loading="lazy";const t=document.createElement("div");t.className=`${a}__main`,this.mainEl=t,this.iconEl=document.createElement("div"),this.iconEl.className=`${a}__icon`,this.labelEl=document.createElement("span"),this.labelEl.className=`${a}__label`,this.labelEl.textContent=e("특별 이벤트"),this.badgeEl=document.createElement("span"),this.badgeEl.className=`${a}__badge`,this.badgeEl.textContent="HOT DEAL",this.titleEl=document.createElement("h2"),this.titleEl.className=`${a}__title`,this.subtitleEl=document.createElement("p"),this.subtitleEl.className=`${a}__subtitle`;const s=document.createElement("div");s.className=`${a}__text`,this.textEl=s,this.countdownWrapEl=document.createElement("div"),this.countdownWrapEl.className=`${a}__countdown`,s.append(this.titleEl,this.subtitleEl),this.actionsEl=document.createElement("div"),this.actionsEl.className=`${a}__actions`,this.ctaEl=document.createElement("db-button"),this.ctaEl.className=`${a}__cta`,this.ctaEl.setAttribute("variant","primary"),this.ctaEl.setAttribute("end-icon","arrow-right"),this.ctaEl.addEventListener("click",t=>{t.stopPropagation(),this.dispatchEvent(new CustomEvent("db-cta-click",{bubbles:!0,composed:!0}))}),this.actionsEl.append(this.ctaEl),t.append(s,this.actionsEl),this.progressEl=document.createElement("div"),this.progressEl.className=`${a}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${a}__progress-bar`,this.progressEl.append(this.progressBarEl),this.contentEl.append(this.closeBtn,t,this.progressEl),this.rootEl.append(this.contentEl),this.append(this.rootEl),this.contentEl.addEventListener("click",t=>{"topbar"===this.getType()&&(t.target?.closest(`.${a}__close`)||this.dispatchEvent(new CustomEvent("db-cta-click",{bubbles:!0,composed:!0})))})}handleClose(){this.visible=!1,this.style.display="none",this.setAttribute("open","false"),this.stopAutoClose(),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))}stopAutoClose(){this.autoCloseTimer&&(window.clearInterval(this.autoCloseTimer),this.autoCloseTimer=void 0)}startAutoClose(t){this.stopAutoClose(),this.progress=100,this.autoCloseTimer=window.setInterval(()=>{this.progress-=100/(t/100),this.progressBarEl&&(this.progressBarEl.style.width=`${Math.max(this.progress,0)}%`),this.progress<=0&&this.handleClose()},100)}getBackground(){const t=this.getAttribute("background");return t&&["solid","gradient","animation","grid"].includes(t)?t:this.getBool("use-random-gradient")?"gradient":"solid"}ensureAnimBg(){if(!this.animBgEl){const t=document.createElement("div");t.className=`${a}__backdrop`;const e=document.createElement("db-animation-background");e.className=`${a}__anim-bg`,e.setAttribute("width","100%"),e.setAttribute("height","100%"),t.append(e),this.animBgEl=t,this.animBgInner=e}return this.animBgEl}ensureGridBg(){if(!this.gridBgEl){const t=document.createElement("div");t.className=`${a}__backdrop`;const e=document.createElement("db-grid-background");e.className=`${a}__grid-bg`,e.setAttribute("width","100%"),e.setAttribute("height","100%"),t.append(e),this.gridBgEl=t,this.gridBgInner=e}return this.gridBgEl}clearBackdrop(t){"gradient"!==t&&this.gradientEl&&(this.gradientEl.remove(),this.gradientEl=void 0),"animation"!==t&&this.animBgEl&&(this.animBgEl.remove(),this.animBgEl=void 0,this.animBgInner=void 0),"grid"!==t&&this.gridBgEl&&(this.gridBgEl.remove(),this.gridBgEl=void 0,this.gridBgInner=void 0)}renderBackground(t,e){if(!this.rootEl||!this.contentEl)return;if(this.clearBackdrop(t),"gradient"===t){this.gradientEl||(this.gradientEl=document.createElement("db-random-gradient"),this.gradientEl.className=`${a}__gradient-bg`,this.gradientEl.setAttribute("height","100%"),this.gradientEl.setAttribute("width","100%"));const t=this.getAttribute("gradient-tone")||"vivid",i=!this.hasAttribute("gradient-animated")||"false"!==this.getAttribute("gradient-animated");return this.gradientEl.setAttribute("scheme",this.getAttribute("gradient-scheme")||g[e]),this.gradientEl.setAttribute("tone",t),this.gradientEl.setAttribute("animated",i?"true":"false"),this.gradientEl.parentElement!==this.rootEl&&this.rootEl.replaceChildren(this.gradientEl),void(this.contentEl.parentElement!==this.gradientEl&&this.gradientEl.append(this.contentEl))}const i=[];if("animation"===t){const t=this.ensureAnimBg();this.animBgInner?.setAttribute("type",this.getAttribute("animation-type")||"graphite"),this.animBgInner?.setAttribute("theme","dark"),i.push(t)}else if("grid"===t){const t=this.ensureGridBg();this.gridBgInner?.setAttribute("type",this.getAttribute("grid-type")||"lines"),this.gridBgInner?.setAttribute("color","var(--db-text-inverse-primary)"),this.gridBgInner?.setAttribute("opacity","0.18"),this.gridBgInner?.setAttribute("fade","vignette"),this.gridBgInner?.setAttribute("animated",""),i.push(t)}const s=[...i,this.contentEl],n=Array.from(this.rootEl.children);n.length===s.length&&s.every((t,e)=>n[e]===t)||this.rootEl.replaceChildren(...s)}syncIcon(t){this.mainEl&&this.textEl&&this.iconEl&&("card"===t?(this.iconEl.replaceChildren(i(d,32)),this.iconEl.parentElement!==this.mainEl&&this.mainEl.insertBefore(this.iconEl,this.textEl)):this.iconEl.remove())}syncBadge(t){this.textEl&&this.badgeEl&&("floating"===t?this.badgeEl.parentElement!==this.textEl&&this.textEl.insertBefore(this.badgeEl,this.textEl.firstChild):this.badgeEl.remove())}syncImage(t,e){this.contentEl&&this.imageEl&&this.mainEl&&("floating"===t&&e?(this.imageEl.src=e,this.imageEl.alt=this.getAttribute("image-alt")||"",this.imageEl.parentElement!==this.contentEl&&this.contentEl.insertBefore(this.imageEl,this.mainEl)):this.imageEl.remove())}syncLink(t,i){if(!this.contentEl)return;const n=s(i,"");if("topbar"!==t||!n)return void this.linkEl?.remove();this.linkEl||(this.linkEl=document.createElement("a"),this.linkEl.className=`${a}__link`),this.linkEl.setAttribute("href",n);const r=this.getAttribute("target");r?this.linkEl.setAttribute("target",r):this.linkEl.removeAttribute("target"),this.linkEl.setAttribute("aria-label",this.titleEl?.textContent||e("배너 링크")),this.linkEl.parentElement!==this.contentEl&&this.contentEl.append(this.linkEl)}syncCountdown(t,e){this.textEl&&this.countdownWrapEl&&(t&&e?(this.countdownEl||(this.countdownEl=document.createElement("db-countdown"),this.countdownEl.setAttribute("size","s"),this.countdownEl.setAttribute("variant","compact"),this.countdownEl.setAttribute("show-labels","false"),this.countdownWrapEl.replaceChildren(this.countdownEl)),this.countdownEl.setAttribute("end-date",e),this.countdownWrapEl.parentElement!==this.textEl&&this.textEl.append(this.countdownWrapEl)):this.countdownWrapEl.remove())}syncLocale(){const t=this.getType();this.titleEl&&!this.getAttribute("title")&&(this.titleEl.textContent=e(o[t])),this.subtitleEl&&!this.getAttribute("subtitle")&&(this.subtitleEl.textContent=e(h[t])),this.ctaEl&&!this.getAttribute("cta-text")&&(this.ctaEl.textContent=e(c[t])),this.labelEl&&(this.labelEl.textContent=e("특별 이벤트")),this.closeBtn?.setAttribute("aria-label",e("닫기")),this.linkEl?.isConnected&&this.linkEl.setAttribute("aria-label",this.titleEl?.textContent||e("배너 링크"))}updateUI(){if(!this.visible||"false"===this.getAttribute("open"))return void(this.style.display="none");this.style.display="block";const t=this.getType(),i=this.getVariant(),s=this.getSize(),n=this.getAttribute("title")||e(o[t]),r=this.getAttribute("subtitle")||e(h[t]),l=this.getAttribute("cta-text")||e(c[t]),d=this.getBool("auto-close"),g=Math.max(Number(this.getAttribute("close-delay")||5e3),1e3),E=this.getBackground(),u=this.getAttribute("gradient-tone")||"vivid",m=this.getBool("show-countdown"),b=this.getAttribute("countdown-end-date");this.rootEl&&(this.rootEl.className=[a,`${a}--type-${t}`,`${a}--variant-${i}`,`${a}--size-${s}`,`${a}--bg-${E}`,d&&`${a}--auto-close`,"gradient"===E&&`${a}--with-gradient`,"gradient"===E&&"light"===u&&`${a}--gradient-light`].filter(Boolean).join(" ")),this.renderBackground(E,i),this.syncIcon(t),this.syncBadge(t),this.syncImage(t,this.getAttribute("image")),this.titleEl&&(this.titleEl.textContent=n),this.subtitleEl&&(this.subtitleEl.textContent=r),this.syncLink(t,this.getAttribute("href")),this.syncCountdown(m,b),this.actionsEl&&(this.actionsEl.hidden="topbar"===t),this.ctaEl&&(this.ctaEl.textContent=l,this.ctaEl.setAttribute("size",this.ctaButtonSize(t,s)),"floating"===t?this.ctaEl.setAttribute("full-width",""):this.ctaEl.removeAttribute("full-width")),this.progressEl&&(this.progressEl.hidden=!d),this.progressBarEl&&d&&(this.progressBarEl.style.width=`${this.progress}%`),this.stopAutoClose(),d&&this.startAutoClose(g)}}customElements.get("db-ad-banner")||customElements.define("db-ad-banner",DbAdBanner);export{DbAdBanner};
@@ -1,4 +1,5 @@
1
1
  import { DbIconName } from './db-icons.js';
2
+ import './db-menu-item.js';
2
3
 
3
4
  interface DbAiChatItem {
4
5
  id: string;
@@ -23,6 +24,7 @@ interface DbAiChatItem {
23
24
  * list.addEventListener('db-select', (e) => console.log(e.detail.id));
24
25
  */
25
26
  declare class DbAiChatList extends HTMLElement {
27
+ private unsubscribeLocale?;
26
28
  static get observedAttributes(): string[];
27
29
  private _items;
28
30
  get items(): DbAiChatItem[];
@@ -30,7 +32,9 @@ declare class DbAiChatList extends HTMLElement {
30
32
  get selectedId(): string;
31
33
  set selectedId(v: string);
32
34
  connectedCallback(): void;
33
- attributeChangedCallback(): void;
35
+ disconnectedCallback(): void;
36
+ attributeChangedCallback(name: string, previous: string | null, next: string | null): void;
37
+ private syncSelection;
34
38
  private render;
35
39
  }
36
40
  declare global {
@@ -1 +1 @@
1
- import{setHostClasses as t}from"../core/host-class.js";import{createDbIcon as e,isDbIconName as s}from"./db-icons.js";class DbAiChatList extends HTMLElement{constructor(){super(...arguments),this._items=[]}static get observedAttributes(){return["selected-id","new-label","title-label"]}get items(){return this._items}set items(t){this._items=Array.isArray(t)?t:[],this.render()}get selectedId(){return this.getAttribute("selected-id")||""}set selectedId(t){t?this.setAttribute("selected-id",t):this.removeAttribute("selected-id")}connectedCallback(){if(!this._items.length&&this.hasAttribute("items"))try{this._items=JSON.parse(this.getAttribute("items")||"[]")}catch{this._items=[]}this.render()}attributeChangedCallback(){this.render()}render(){t(this,["designbase-wc-ai-chat-list"]),this.replaceChildren();const i=document.createElement("button");i.type="button",i.className="designbase-wc-ai-chat-list__new";const n=document.createElement("span");n.className="designbase-wc-ai-chat-list__new-icon",n.append(e("plus",18));const a=document.createElement("span");if(a.textContent=this.getAttribute("new-label")||"새 대화",i.append(n,a),i.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-new",{bubbles:!0,composed:!0}))}),this.append(i),!this._items.length)return;const c=new Map,d=this.getAttribute("title-label")||"최근 항목";for(const t of this._items){const e=t.group||d;c.has(e)||c.set(e,[]),c.get(e).push(t)}const l=this.selectedId,o=document.createElement("div");o.className="designbase-wc-ai-chat-list__scroll";for(const[t,i]of c){const n=document.createElement("div");n.className="designbase-wc-ai-chat-list__group";const a=document.createElement("div");a.className="designbase-wc-ai-chat-list__group-title",a.textContent=t,n.append(a);for(const t of i){const i=document.createElement("button");if(i.type="button",i.className=["designbase-wc-ai-chat-list__item",t.id===l&&"designbase-wc-ai-chat-list__item--selected"].filter(Boolean).join(" "),t.icon&&s(t.icon)){const s=document.createElement("span");s.className="designbase-wc-ai-chat-list__item-icon",s.append(e(t.icon,16)),i.append(s)}const a=document.createElement("span");a.className="designbase-wc-ai-chat-list__item-title",a.textContent=t.title,i.append(a),i.addEventListener("click",()=>{this.selectedId=t.id,this.dispatchEvent(new CustomEvent("db-select",{detail:{id:t.id,title:t.title},bubbles:!0,composed:!0}))}),n.append(i)}o.append(n)}this.append(o)}}customElements.get("db-ai-chat-list")||customElements.define("db-ai-chat-list",DbAiChatList);export{DbAiChatList};
1
+ import{setHostClasses as t}from"../core/host-class.js";import{subscribeDbLocale as e,dbText as s}from"../core/i18n.js";import{isDbIconName as i}from"./db-icons.js";import"./db-menu-item.js";class DbAiChatList extends HTMLElement{constructor(){super(...arguments),this._items=[]}static get observedAttributes(){return["selected-id","new-label","title-label"]}get items(){return this._items}set items(t){this._items=Array.isArray(t)?t:[],this.render()}get selectedId(){return this.getAttribute("selected-id")||""}set selectedId(t){t?this.setAttribute("selected-id",t):this.removeAttribute("selected-id")}connectedCallback(){if(this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>{const t=document.activeElement instanceof HTMLElement&&this.contains(document.activeElement)?document.activeElement:null,e=!!t?.closest("[data-chat-new]"),s=t?.closest("[data-chat-id]")?.dataset.chatId,i=this.querySelector(".designbase-wc-ai-chat-list__scroll")?.scrollTop??0;this.render();const c=this.querySelector(".designbase-wc-ai-chat-list__scroll");c&&(c.scrollTop=i),e?this.querySelector("[data-chat-new] button")?.focus():s&&Array.from(this.querySelectorAll("[data-chat-id]")).find(t=>t.dataset.chatId===s)?.querySelector("button")?.focus()}),!this._items.length&&this.hasAttribute("items"))try{this._items=JSON.parse(this.getAttribute("items")||"[]")}catch{this._items=[]}this.render()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(t,e,s){this.isConnected&&e!==s&&("selected-id"===t?this.syncSelection():this.render())}syncSelection(){const t=document.activeElement instanceof HTMLElement?document.activeElement.closest("[data-chat-id]"):null;for(const t of this.querySelectorAll("db-menu-item[data-chat-id]")){const e=t.dataset.chatId===this.selectedId;t.hasAttribute("active")!==e&&t.toggleAttribute("active",e),e&&t.querySelector("button")?.setAttribute("aria-current","true")}t&&this.contains(t)&&t.querySelector("button")?.focus({preventScroll:!0})}render(){t(this,["designbase-wc-ai-chat-list"]),this.replaceChildren();const e=document.createElement("db-menu-item");if(e.setAttribute("data-chat-new",""),e.setAttribute("label",this.getAttribute("new-label")||s("새 대화")),e.setAttribute("icon","plus"),e.setAttribute("size","m"),e.addEventListener("db-click",t=>{t.stopPropagation(),this.dispatchEvent(new CustomEvent("db-new",{bubbles:!0,composed:!0}))}),this.append(e),!this._items.length)return;const c=new Map,n=this.getAttribute("title-label")||s("최근 항목");for(const t of this._items){const e=t.group||n;c.has(e)||c.set(e,[]),c.get(e).push(t)}const a=this.selectedId,o=document.createElement("div");o.className="designbase-wc-ai-chat-list__scroll";for(const[t,e]of c){const s=document.createElement("div");s.className="designbase-wc-ai-chat-list__group";const c=document.createElement("div");c.className="designbase-wc-ai-chat-list__group-title",c.textContent=t,s.append(c);for(const t of e){const e=document.createElement("db-menu-item");e.dataset.chatId=t.id,e.setAttribute("label",t.title),e.setAttribute("size","m"),t.id===a&&e.setAttribute("active",""),t.icon&&i(t.icon)&&e.setAttribute("icon",t.icon),e.addEventListener("db-click",e=>{e.stopPropagation(),this.selectedId=t.id,this.dispatchEvent(new CustomEvent("db-select",{detail:{id:t.id,title:t.title},bubbles:!0,composed:!0}))}),s.append(e)}o.append(s)}this.append(o),this.syncSelection()}}customElements.get("db-ai-chat-list")||customElements.define("db-ai-chat-list",DbAiChatList);export{DbAiChatList};
@@ -70,6 +70,7 @@ interface DbChatMessageData {
70
70
  declare class DbAiChat extends HTMLElement {
71
71
  static get observedAttributes(): string[];
72
72
  private built;
73
+ private unsubscribeLocale?;
73
74
  private typing;
74
75
  private _messages;
75
76
  private _suggestions;
@@ -95,6 +96,7 @@ declare class DbAiChat extends HTMLElement {
95
96
  set attachOptions(v: DbChatAttachOption[]);
96
97
  get attachOptions(): DbChatAttachOption[];
97
98
  connectedCallback(): void;
99
+ disconnectedCallback(): void;
98
100
  attributeChangedCallback(name: string): void;
99
101
  private build;
100
102
  private buildArtifact;
@@ -1 +1 @@
1
- import{dbText as t,dbFormat as e}from"../core/i18n.js";import"./db-chat-welcome.js";import"./db-chat-suggestions.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-chat-composer.js";import{createDbIcon as i,isDbIconName as s}from"./db-icons.js";import{sanitizeHtmlFragment as a}from"../core/safe-html.js";const r="designbase-wc-ai-chat";let c=0;function el(t,e,i){const s=document.createElement(t);return e&&(s.className=e),null!=i&&(s.textContent=i),s}class DbAiChat extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this._messages=[],this._suggestions=[],this._tools=[],this._attachOptions=null,this.threadEl=null,this.activeArtifactId=null,this.artifactSeq=0,this.onSend=t=>{t.stopPropagation();const e=t.detail;this.dispatchEvent(new CustomEvent("db-send",{detail:e,bubbles:!0,composed:!0}))},this.onSuggestionSelect=t=>{t.stopPropagation();const e=t.detail,i=new CustomEvent("db-suggestion-select",{detail:e,bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(i)&&(this.composer.value=e.value,this.composer.focusInput())}}static get observedAttributes(){return["greeting","title","highlight","subtitle","placeholder","orb-variant","suggestions-layout","suggestions-columns","disclaimer","voice","voice-state","voice-message","voice-disabled","attach","max-width","artifact-open","artifact-title"]}set messages(t){this._messages=Array.isArray(t)?t.map(t=>({id:`m${c+=1}`,...t})):[],this.built&&this.renderContent()}get messages(){return this._messages}set suggestions(t){this._suggestions=Array.isArray(t)?t:[],this.built&&0===this._messages.length&&this.renderContent()}get suggestions(){return this._suggestions}set tools(t){this._tools=Array.isArray(t)?t:[],this.built&&(this.composer.tools=this._tools)}get tools(){return this._tools}set attachOptions(t){this._attachOptions=Array.isArray(t)?t:null,this.built&&this._attachOptions&&(this.composer.attachOptions=this._attachOptions)}get attachOptions(){return this._attachOptions??this.composer?.attachOptions??[]}connectedCallback(){this.built||this.build(),this.sync()}attributeChangedCallback(t){this.built&&("placeholder"!==t?["voice","attach","voice-state","voice-message","voice-disabled"].includes(t)?this.syncComposerToggles():"disclaimer"!==t?"artifact-open"!==t?"artifact-title"!==t?this.sync():this.artifactTitleEl.textContent=this.getAttribute("artifact-title")||"":this.syncArtifact(!0):this.renderDock():this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||""))}build(){this.built=!0,this.classList.add(r),this.replaceChildren(),this.mainEl=el("div",`${r}__main`),this.scrollEl=el("div",`${r}__scroll`),this.dockEl=el("div",`${r}__dock`),this.composer=document.createElement("db-chat-composer"),this.composer.className=`${r}__composer`,this.composer.addEventListener("db-send",this.onSend),this.composer.addEventListener("db-stop",t=>this.relay(t,"db-stop")),this.composer.addEventListener("db-tool-toggle",t=>this.relay(t,"db-tool-toggle")),this.composer.addEventListener("db-attach",t=>this.relay(t,"db-attach")),this.composer.addEventListener("db-attach-files",t=>this.relay(t,"db-attach-files")),this.composer.addEventListener("db-voice",t=>this.relay(t,"db-voice")),this.mainEl.append(this.scrollEl,this.dockEl),this.buildArtifact(),this.append(this.mainEl,this.artifactEl)}buildArtifact(){this.artifactEl=el("aside",`${r}__artifact`),this.artifactEl.setAttribute("aria-hidden","true");const e=el("div",`${r}__artifact-panel`),s=el("div",`${r}__artifact-header`),a=el("div",`${r}__artifact-titles`);this.artifactTitleEl=el("div",`${r}__artifact-title`),this.artifactSubtitleEl=el("div",`${r}__artifact-subtitle`),this.artifactSubtitleEl.hidden=!0,a.append(this.artifactTitleEl,this.artifactSubtitleEl);const c=el("button",`${r}__artifact-close`);c.type="button",c.setAttribute("aria-label",t("아티팩트 닫기")),c.append(i("close",18)),c.addEventListener("click",()=>this.closeArtifact()),s.append(a,c),this.artifactBodyEl=el("div",`${r}__artifact-body`),e.append(s,this.artifactBodyEl),this.artifactEl.append(e)}sync(){const t=this.getAttribute("max-width")||"100%";this.style.setProperty("--dbchat-max",t),this.syncComposerToggles(),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"무엇이든 물어보세요…"),this._tools.length&&(this.composer.tools=this._tools),this._attachOptions&&(this.composer.attachOptions=this._attachOptions);const e=this.getAttribute("artifact-title");null!=e&&(this.artifactTitleEl.textContent=e),this.renderContent(),this.renderDock(),this.syncArtifact(!1)}syncArtifact(t){const e=this.hasAttribute("artifact-open");this.classList.toggle(`${r}--artifact-open`,e),this.artifactEl.setAttribute("aria-hidden",String(!e)),this.artifactEl.toggleAttribute("inert",!e),e||(this.activeArtifactId=null),this.syncArtifactCardStates(),t&&this.dispatchEvent(new CustomEvent(e?"db-artifact-open":"db-artifact-close",{detail:e?{id:this.activeArtifactId}:{id:null},bubbles:!0,composed:!0}))}syncComposerToggles(){this.composer.toggleAttribute("voice",this.hasAttribute("voice")),this.composer.setAttribute("voice-state",this.getAttribute("voice-state")||"idle"),this.composer.setAttribute("voice-message",this.getAttribute("voice-message")||""),this.composer.toggleAttribute("voice-disabled",this.hasAttribute("voice-disabled")),this.composer.toggleAttribute("attach","false"!==this.getAttribute("attach"))}renderContent(){this.scrollEl.replaceChildren(),this.threadEl=null;const t=0===this._messages.length;this.classList.toggle(`${r}--empty`,t),t?this.renderHero():this.renderThread()}renderHero(){const t=el("div",`${r}__hero`),e=document.createElement("db-chat-welcome");if(e.className=`${r}__welcome`,this.getAttribute("greeting")&&e.setAttribute("greeting",this.getAttribute("greeting")),e.setAttribute("title",this.getAttribute("title")||"무엇을 도와드릴까요?"),this.getAttribute("highlight")&&e.setAttribute("highlight",this.getAttribute("highlight")),this.getAttribute("subtitle")&&e.setAttribute("subtitle",this.getAttribute("subtitle")),e.setAttribute("orb-variant",this.getAttribute("orb-variant")||"aurora"),e.setAttribute("align","center"),t.append(e),this._suggestions.length){const e=document.createElement("db-chat-suggestions");e.className=`${r}__suggestions`,e.setAttribute("layout",this.getAttribute("suggestions-layout")||"card"),e.setAttribute("columns",this.getAttribute("suggestions-columns")||"3"),e.items=this._suggestions,e.addEventListener("db-select",this.onSuggestionSelect),t.append(e)}this.scrollEl.append(t)}renderThread(){const t=el("div",`${r}__thread`);this._messages.forEach(e=>t.append(this.buildMessage(e))),this.threadEl=t,this.scrollEl.append(t),this.typing&&t.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())}buildMessage(t){const e=el("div",`${r}__turn`);e.dataset.id=t.id||"";const i=el("db-chat-message");return i.dataset.id=t.id||"",i.setAttribute("role",t.role||"assistant"),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time),t.avatarSrc&&i.setAttribute("avatar-src",t.avatarSrc),t.avatarInitials&&i.setAttribute("avatar-initials",t.avatarInitials),t.actions&&i.setAttribute("actions",t.actions),t.status&&i.setAttribute("status",t.status),t.variant&&i.setAttribute("variant",t.variant),null!=t.html?i.replaceChildren(a(t.html)):i.textContent=t.text||"",i.addEventListener("db-action",t=>this.relay(t,"db-message-action")),e.append(i),t.artifact&&(this.ensureArtifactId(t.artifact),e.append(this.buildArtifactCard(t.artifact))),e}ensureArtifactId(t){return t.id||(t.id=`art-${this.artifactSeq+=1}`),t.id}buildArtifactCard(t){const a=this.ensureArtifactId(t),c=el("button",`${r}__artifact-card`);c.type="button",c.dataset.artifactId=a,c.setAttribute("aria-pressed","false"),c.setAttribute("aria-label",e("{0} 아티팩트 열기",[t.title]));const n=el("span",`${r}__artifact-card-icon`),l=t.icon&&s(t.icon)?t.icon:"code";n.append(i(l,18));const o=el("div",`${r}__artifact-card-meta`);o.append(el("div",`${r}__artifact-card-title`,t.title)),t.subtitle&&o.append(el("div",`${r}__artifact-card-subtitle`,t.subtitle));const h=el("span",`${r}__artifact-card-chevron`);return h.append(i("chevron-right",16)),c.append(n,o,h),c.addEventListener("click",()=>this.toggleArtifactFromCard(t)),c}syncArtifactCardStates(){this.querySelectorAll(`.${r}__artifact-card`).forEach(t=>{const e=t,i=this.artifactOpen&&e.dataset.artifactId===this.activeArtifactId;e.classList.toggle(`${r}__artifact-card--active`,i),e.setAttribute("aria-pressed",String(i))})}toggleArtifactFromCard(t){const e=this.ensureArtifactId(t);this.artifactOpen&&this.activeArtifactId===e?this.closeArtifact():this.openArtifact({id:e,title:t.title,subtitle:t.subtitle,html:t.html})}buildTyping(){const t=el("db-chat-typing",`${r}__typing`),e=this.getAttribute("title")||"AI";return t.setAttribute("name",this.getAttribute("agent-name")||e),t.setAttribute("label","생각 중…"),t.setAttribute("avatar-initials","AI"),t}renderDock(){this.dockEl.replaceChildren(),this.dockEl.append(this.composer);const t=this.getAttribute("disclaimer");t&&this.dockEl.append(el("p",`${r}__disclaimer`,t))}relay(t,e){t.stopPropagation(),this.dispatchEvent(new CustomEvent(e,{detail:t.detail,bubbles:!0,composed:!0}))}scrollToEnd(){this.scrollEl.scrollTop=this.scrollEl.scrollHeight}addMessage(t){const e={id:`m${c+=1}`,...t},i=0===this._messages.length;if(this._messages.push(e),i||!this.threadEl)this.renderContent();else{const t=this.threadEl.querySelector("db-chat-typing"),i=this.buildMessage(e);t?this.threadEl.insertBefore(i,t):this.threadEl.append(i),requestAnimationFrame(()=>this.scrollToEnd())}return e.id}updateMessage(t,e){const i=this._messages.find(e=>e.id===t);if(!i)return;Object.assign(i,e);const s=this.threadEl?.querySelector(`db-chat-message[data-id="${t}"]`);if(!s)return;const c=s.querySelector(".designbase-wc-chat-message__content");if(!c||null==e.html&&null==e.text||(null!=e.html?c.replaceChildren(a(e.html)):c.textContent=e.text||""),e.status&&s.setAttribute("status",e.status),void 0!==e.artifact){const t=s.closest(`.${r}__turn`)||s.parentElement;t?.querySelector(`.${r}__artifact-card`)?.remove(),e.artifact&&t&&(this.ensureArtifactId(e.artifact),t.append(this.buildArtifactCard(e.artifact)),this.syncArtifactCardStates())}requestAnimationFrame(()=>this.scrollToEnd())}appendToMessage(t,e){const i=this._messages.find(e=>e.id===t);if(!i)return;i.text=(i.text||"")+e;const s=this.threadEl?.querySelector(`db-chat-message[data-id="${t}"]`),a=s?.querySelector(".designbase-wc-chat-message__content");a&&(a.textContent=i.text),requestAnimationFrame(()=>this.scrollToEnd())}setMessages(t){this.messages=t}clear(){this._messages=[],this.typing=!1,this.closeArtifact(),this.built&&this.renderContent()}setTyping(t){if(this.typing=Boolean(t),!this.built||!this.threadEl)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())):!this.typing&&e&&e.remove()}setStreaming(t){this.composer?.setStreaming(Boolean(t))}focusInput(){this.composer?.focusInput()}getComposer(){return this.composer}get artifactOpen(){return this.hasAttribute("artifact-open")}set artifactOpen(t){this.toggleAttribute("artifact-open",t)}openArtifact(t={}){null!=t.title&&(this.artifactTitleEl.textContent=t.title),null!=t.subtitle&&(this.artifactSubtitleEl.textContent=t.subtitle,this.artifactSubtitleEl.hidden=!t.subtitle),null!=t.html&&this.artifactBodyEl.replaceChildren(a(t.html)),null!=t.id&&(this.activeArtifactId=t.id);const e=this.artifactOpen;this.artifactOpen=!0,e&&this.syncArtifactCardStates()}closeArtifact(){this.activeArtifactId=null;const t=this.artifactOpen;this.artifactOpen=!1,t||this.syncArtifactCardStates()}toggleArtifact(){this.artifactOpen?this.closeArtifact():this.artifactOpen=!0}getArtifactBody(){return this.artifactBodyEl}setArtifactContent(t){this.artifactBodyEl.replaceChildren(t)}}customElements.get("db-ai-chat")||customElements.define("db-ai-chat",DbAiChat);export{DbAiChat};
1
+ import{subscribeDbLocale as t,dbText as e,dbFormat as i}from"../core/i18n.js";import"./db-chat-welcome.js";import"./db-chat-suggestions.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-chat-composer.js";import{createDbIcon as s,isDbIconName as a}from"./db-icons.js";import{sanitizeHtmlFragment as r}from"../core/safe-html.js";const c="designbase-wc-ai-chat";let l=0;function el(t,e,i){const s=document.createElement(t);return e&&(s.className=e),null!=i&&(s.textContent=i),s}class DbAiChat extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this._messages=[],this._suggestions=[],this._tools=[],this._attachOptions=null,this.threadEl=null,this.activeArtifactId=null,this.artifactSeq=0,this.onSend=t=>{t.stopPropagation();const e=t.detail;this.dispatchEvent(new CustomEvent("db-send",{detail:e,bubbles:!0,composed:!0}))},this.onSuggestionSelect=t=>{t.stopPropagation();const e=t.detail,i=new CustomEvent("db-suggestion-select",{detail:e,bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(i)&&(this.composer.value=e.value,this.composer.focusInput())}}static get observedAttributes(){return["greeting","title","highlight","subtitle","placeholder","orb-variant","suggestions-layout","suggestions-columns","disclaimer","voice","voice-state","voice-message","voice-disabled","attach","max-width","artifact-open","artifact-title"]}set messages(t){this._messages=Array.isArray(t)?t.map(t=>({id:`m${l+=1}`,...t})):[],this.built&&this.renderContent()}get messages(){return this._messages}set suggestions(t){this._suggestions=Array.isArray(t)?t:[],this.built&&0===this._messages.length&&this.renderContent()}get suggestions(){return this._suggestions}set tools(t){this._tools=Array.isArray(t)?t:[],this.built&&(this.composer.tools=this._tools)}get tools(){return this._tools}set attachOptions(t){this._attachOptions=Array.isArray(t)?t:null,this.built&&this._attachOptions&&(this.composer.attachOptions=this._attachOptions)}get attachOptions(){return this._attachOptions??this.composer?.attachOptions??[]}connectedCallback(){this.built||this.build(),this.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>{this.hasAttribute("placeholder")||this.composer.setAttribute("placeholder",e("무엇이든 물어보세요…")),this.hasAttribute("title")||this.querySelector(`.${c}__welcome`)?.setAttribute("title",e("무엇을 도와드릴까요?")),this.artifactEl.querySelector(`.${c}__artifact-close`)?.setAttribute("aria-label",e("아티팩트 닫기"))}),this.sync()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(t){this.built&&("placeholder"!==t?["voice","attach","voice-state","voice-message","voice-disabled"].includes(t)?this.syncComposerToggles():"disclaimer"!==t?"artifact-open"!==t?"artifact-title"!==t?this.sync():this.artifactTitleEl.textContent=this.getAttribute("artifact-title")||"":this.syncArtifact(!0):this.renderDock():this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||e("무엇이든 물어보세요…")))}build(){this.built=!0,this.classList.add(c),this.replaceChildren(),this.mainEl=el("div",`${c}__main`),this.scrollEl=el("div",`${c}__scroll`),this.dockEl=el("div",`${c}__dock`),this.composer=document.createElement("db-chat-composer"),this.composer.className=`${c}__composer`,this.composer.addEventListener("db-send",this.onSend),this.composer.addEventListener("db-stop",t=>this.relay(t,"db-stop")),this.composer.addEventListener("db-tool-toggle",t=>this.relay(t,"db-tool-toggle")),this.composer.addEventListener("db-attach",t=>this.relay(t,"db-attach")),this.composer.addEventListener("db-attach-files",t=>this.relay(t,"db-attach-files")),this.composer.addEventListener("db-voice",t=>this.relay(t,"db-voice")),this.mainEl.append(this.scrollEl,this.dockEl),this.buildArtifact(),this.append(this.mainEl,this.artifactEl)}buildArtifact(){this.artifactEl=el("aside",`${c}__artifact`),this.artifactEl.setAttribute("aria-hidden","true");const t=el("div",`${c}__artifact-panel`),i=el("div",`${c}__artifact-header`),a=el("div",`${c}__artifact-titles`);this.artifactTitleEl=el("div",`${c}__artifact-title`),this.artifactSubtitleEl=el("div",`${c}__artifact-subtitle`),this.artifactSubtitleEl.hidden=!0,a.append(this.artifactTitleEl,this.artifactSubtitleEl);const r=el("button",`${c}__artifact-close`);r.type="button",r.setAttribute("aria-label",e("아티팩트 닫기")),r.append(s("close",18)),r.addEventListener("click",()=>this.closeArtifact()),i.append(a,r),this.artifactBodyEl=el("div",`${c}__artifact-body`),t.append(i,this.artifactBodyEl),this.artifactEl.append(t)}sync(){const t=this.getAttribute("max-width")||"100%";this.style.setProperty("--dbchat-max",t),this.syncComposerToggles(),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||e("무엇이든 물어보세요…")),this._tools.length&&(this.composer.tools=this._tools),this._attachOptions&&(this.composer.attachOptions=this._attachOptions);const i=this.getAttribute("artifact-title");null!=i&&(this.artifactTitleEl.textContent=i),this.renderContent(),this.renderDock(),this.syncArtifact(!1)}syncArtifact(t){const e=this.hasAttribute("artifact-open");this.classList.toggle(`${c}--artifact-open`,e),this.artifactEl.setAttribute("aria-hidden",String(!e)),this.artifactEl.toggleAttribute("inert",!e),e||(this.activeArtifactId=null),this.syncArtifactCardStates(),t&&this.dispatchEvent(new CustomEvent(e?"db-artifact-open":"db-artifact-close",{detail:e?{id:this.activeArtifactId}:{id:null},bubbles:!0,composed:!0}))}syncComposerToggles(){this.composer.toggleAttribute("voice",this.hasAttribute("voice")),this.composer.setAttribute("voice-state",this.getAttribute("voice-state")||"idle"),this.composer.setAttribute("voice-message",this.getAttribute("voice-message")||""),this.composer.toggleAttribute("voice-disabled",this.hasAttribute("voice-disabled")),this.composer.toggleAttribute("attach","false"!==this.getAttribute("attach"))}renderContent(){this.scrollEl.replaceChildren(),this.threadEl=null;const t=0===this._messages.length;this.classList.toggle(`${c}--empty`,t),t?this.renderHero():this.renderThread()}renderHero(){const t=el("div",`${c}__hero`),i=document.createElement("db-chat-welcome");if(i.className=`${c}__welcome`,this.getAttribute("greeting")&&i.setAttribute("greeting",this.getAttribute("greeting")),i.setAttribute("title",this.getAttribute("title")||e("무엇을 도와드릴까요?")),this.getAttribute("highlight")&&i.setAttribute("highlight",this.getAttribute("highlight")),this.getAttribute("subtitle")&&i.setAttribute("subtitle",this.getAttribute("subtitle")),i.setAttribute("orb-variant",this.getAttribute("orb-variant")||"aurora"),i.setAttribute("align","center"),t.append(i),this._suggestions.length){const e=document.createElement("db-chat-suggestions");e.className=`${c}__suggestions`,e.setAttribute("layout",this.getAttribute("suggestions-layout")||"card"),e.setAttribute("columns",this.getAttribute("suggestions-columns")||"3"),e.items=this._suggestions,e.addEventListener("db-select",this.onSuggestionSelect),t.append(e)}this.scrollEl.append(t)}renderThread(){const t=el("div",`${c}__thread`);this._messages.forEach(e=>t.append(this.buildMessage(e))),this.threadEl=t,this.scrollEl.append(t),this.typing&&t.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())}buildMessage(t){const e=el("div",`${c}__turn`);e.dataset.id=t.id||"";const i=el("db-chat-message");return i.dataset.id=t.id||"",i.setAttribute("role",t.role||"assistant"),t.name&&i.setAttribute("name",t.name),t.time&&i.setAttribute("timestamp",t.time),t.avatarSrc&&i.setAttribute("avatar-src",t.avatarSrc),t.avatarInitials&&i.setAttribute("avatar-initials",t.avatarInitials),t.actions&&i.setAttribute("actions",t.actions),t.status&&i.setAttribute("status",t.status),t.variant&&i.setAttribute("variant",t.variant),null!=t.html?i.replaceChildren(r(t.html)):i.textContent=t.text||"",i.addEventListener("db-action",t=>this.relay(t,"db-message-action")),e.append(i),t.artifact&&(this.ensureArtifactId(t.artifact),e.append(this.buildArtifactCard(t.artifact))),e}ensureArtifactId(t){return t.id||(t.id=`art-${this.artifactSeq+=1}`),t.id}buildArtifactCard(t){const e=this.ensureArtifactId(t),r=el("button",`${c}__artifact-card`);r.type="button",r.dataset.artifactId=e,r.setAttribute("aria-pressed","false"),r.setAttribute("aria-label",i("{0} 아티팩트 열기",[t.title]));const l=el("span",`${c}__artifact-card-icon`),n=t.icon&&a(t.icon)?t.icon:"code";l.append(s(n,18));const o=el("div",`${c}__artifact-card-meta`);o.append(el("div",`${c}__artifact-card-title`,t.title)),t.subtitle&&o.append(el("div",`${c}__artifact-card-subtitle`,t.subtitle));const h=el("span",`${c}__artifact-card-chevron`);return h.append(s("chevron-right",16)),r.append(l,o,h),r.addEventListener("click",()=>this.toggleArtifactFromCard(t)),r}syncArtifactCardStates(){this.querySelectorAll(`.${c}__artifact-card`).forEach(t=>{const e=t,i=this.artifactOpen&&e.dataset.artifactId===this.activeArtifactId;e.classList.toggle(`${c}__artifact-card--active`,i),e.setAttribute("aria-pressed",String(i))})}toggleArtifactFromCard(t){const e=this.ensureArtifactId(t);this.artifactOpen&&this.activeArtifactId===e?this.closeArtifact():this.openArtifact({id:e,title:t.title,subtitle:t.subtitle,html:t.html})}buildTyping(){const t=el("db-chat-typing",`${c}__typing`),e=this.getAttribute("title")||"AI";return t.setAttribute("name",this.getAttribute("agent-name")||e),t.setAttribute("label","생각 중…"),t.setAttribute("avatar-initials","AI"),t}renderDock(){this.dockEl.replaceChildren(),this.dockEl.append(this.composer);const t=this.getAttribute("disclaimer");t&&this.dockEl.append(el("p",`${c}__disclaimer`,t))}relay(t,e){t.stopPropagation(),this.dispatchEvent(new CustomEvent(e,{detail:t.detail,bubbles:!0,composed:!0}))}scrollToEnd(){this.scrollEl.scrollTop=this.scrollEl.scrollHeight}addMessage(t){const e={id:`m${l+=1}`,...t},i=0===this._messages.length;if(this._messages.push(e),i||!this.threadEl)this.renderContent();else{const t=this.threadEl.querySelector("db-chat-typing"),i=this.buildMessage(e);t?this.threadEl.insertBefore(i,t):this.threadEl.append(i),requestAnimationFrame(()=>this.scrollToEnd())}return e.id}updateMessage(t,e){const i=this._messages.find(e=>e.id===t);if(!i)return;Object.assign(i,e);const s=this.threadEl?.querySelector(`db-chat-message[data-id="${t}"]`);if(!s)return;const a=s.querySelector(".designbase-wc-chat-message__content");if(!a||null==e.html&&null==e.text||(null!=e.html?a.replaceChildren(r(e.html)):a.textContent=e.text||""),e.status&&s.setAttribute("status",e.status),void 0!==e.artifact){const t=s.closest(`.${c}__turn`)||s.parentElement;t?.querySelector(`.${c}__artifact-card`)?.remove(),e.artifact&&t&&(this.ensureArtifactId(e.artifact),t.append(this.buildArtifactCard(e.artifact)),this.syncArtifactCardStates())}requestAnimationFrame(()=>this.scrollToEnd())}appendToMessage(t,e){const i=this._messages.find(e=>e.id===t);if(!i)return;i.text=(i.text||"")+e;const s=this.threadEl?.querySelector(`db-chat-message[data-id="${t}"]`),a=s?.querySelector(".designbase-wc-chat-message__content");a&&(a.textContent=i.text),requestAnimationFrame(()=>this.scrollToEnd())}setMessages(t){this.messages=t}clear(){this._messages=[],this.typing=!1,this.closeArtifact(),this.built&&this.renderContent()}setTyping(t){if(this.typing=Boolean(t),!this.built||!this.threadEl)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())):!this.typing&&e&&e.remove()}setStreaming(t){this.composer?.setStreaming(Boolean(t))}focusInput(){this.composer?.focusInput()}getComposer(){return this.composer}get artifactOpen(){return this.hasAttribute("artifact-open")}set artifactOpen(t){this.toggleAttribute("artifact-open",t)}openArtifact(t={}){null!=t.title&&(this.artifactTitleEl.textContent=t.title),null!=t.subtitle&&(this.artifactSubtitleEl.textContent=t.subtitle,this.artifactSubtitleEl.hidden=!t.subtitle),null!=t.html&&this.artifactBodyEl.replaceChildren(r(t.html)),null!=t.id&&(this.activeArtifactId=t.id);const e=this.artifactOpen;this.artifactOpen=!0,e&&this.syncArtifactCardStates()}closeArtifact(){this.activeArtifactId=null;const t=this.artifactOpen;this.artifactOpen=!1,t||this.syncArtifactCardStates()}toggleArtifact(){this.artifactOpen?this.closeArtifact():this.artifactOpen=!0}getArtifactBody(){return this.artifactBodyEl}setArtifactContent(t){this.artifactBodyEl.replaceChildren(t)}}customElements.get("db-ai-chat")||customElements.define("db-ai-chat",DbAiChat);export{DbAiChat};
@@ -1 +1 @@
1
- import{t as e}from"../core/i18n.js";import{DbAiDataElement as t,aiNode as i}from"../core/ai-element.js";class DbAiFileTree extends t{static get observedAttributes(){return["label","items","disabled","selected-id"]}view(){const build=(t,r=[])=>{const s=i("ul","designbase-wc-ai__files","");for(const e of t){const t=document.createElement("li");if(e.children){const i=this.disclosure(`folder:${[...r,e.id].map(encodeURIComponent).join("/")}`,e.label,"complete",[build(e.children,[...r,e.id])],!0,"folder"),s=i.querySelector(".designbase-wc-ai__icon");s?.removeAttribute("aria-label"),s?.setAttribute("aria-hidden","true");const l=i.querySelector("summary"),n=l.lastElementChild;n.classList.add("designbase-wc-ai__folder-chevron"),l.prepend(n),t.append(i)}else{const i=this.button("file-open",e.label,"file-new",e.id);e.id===this.getAttribute("selected-id")&&(i.setAttribute("variant","secondary"),i.setAttribute("aria-current","true")),t.append(i)}s.append(t)}return t.length||s.append(i("li","designbase-wc-ai__empty-folder",e("aiElement.emptyFolder"))),s};if(!this.rows.length)return[i("p","designbase-wc-ai__description",e("aiElement.noFiles"))];const t=i("div","designbase-wc-ai__file-tree","");return t.setAttribute("aria-label",this.attr("label","files")),t.append(build(this.rows)),[t]}}customElements.get("db-ai-file-tree")||customElements.define("db-ai-file-tree",DbAiFileTree);export{DbAiFileTree};
1
+ import{t as e}from"../core/i18n.js";import{DbAiDataElement as t,aiNode as i}from"../core/ai-element.js";class DbAiFileTree extends t{static get observedAttributes(){return["label","items","disabled","selected-id"]}view(){const build=(t,r=[])=>{const s=i("ul","designbase-wc-ai__files","");for(const e of t){const t=document.createElement("li");if(e.children){const i=this.disclosure(`folder:${[...r,e.id].map(encodeURIComponent).join("/")}`,e.label,"complete",[build(e.children,[...r,e.id])],!0,"folder"),s=i.querySelector(".designbase-wc-ai__icon");s?.removeAttribute("aria-label"),s?.setAttribute("aria-hidden","true");const n=i.querySelector("summary"),l=n.lastElementChild;l.classList.add("designbase-wc-ai__folder-chevron"),n.prepend(l),t.append(i)}else{const i=this.button("file-open",e.label,"file-new",e.id),r=document.createElement("span");r.className="designbase-wc-ai__file-name",r.textContent=e.label,i.title=e.label,Array.from(i.childNodes).filter(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()).forEach(e=>e.remove()),i.append(r),e.id===this.getAttribute("selected-id")&&(i.setAttribute("variant","secondary"),i.setAttribute("aria-current","true")),t.append(i)}s.append(t)}return t.length||s.append(i("li","designbase-wc-ai__empty-folder",e("aiElement.emptyFolder"))),s};if(!this.rows.length)return[i("p","designbase-wc-ai__description",e("aiElement.noFiles"))];const t=i("div","designbase-wc-ai__file-tree","");return t.setAttribute("aria-label",this.attr("label","files")),t.append(build(this.rows)),[t]}}customElements.get("db-ai-file-tree")||customElements.define("db-ai-file-tree",DbAiFileTree);export{DbAiFileTree};
@@ -7,6 +7,7 @@ interface DbAlertAction {
7
7
  disabled?: boolean;
8
8
  }
9
9
  declare class DbAlert extends HTMLElement {
10
+ private unsubscribeLocale?;
10
11
  static get observedAttributes(): string[];
11
12
  /** 소비자가 넣은 자식(링크·코드 등)을 담아 두는 내부 컨테이너. 렌더 사이에 유지된다. */
12
13
  private slotEl?;